Angular 18
Angular 18
Here is an example of how you can implement CRUD (Create,
Read, Update, Delete) operations in Angular 18, with a focus on Angular code.
This assumes that your backend API (using .NET Core) is already set up and
available. Angular will interact with this API to perform CRUD operations.
1. Create Angular Service for CRUD Operations
// crud.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from
'@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CrudService {
private apiUrl =
'https://your-api-url/api/items'; // Replace with your .NET Core API endpoint
constructor(private
http: HttpClient) { }
// Get all items
getItems():
Observable<any[]> {
return
this.http.get<any[]>(this.apiUrl);
}
// Get a single item
by ID
getItemById(id:
number): Observable<any> {
return
this.http.get<any>(`${this.apiUrl}/${id}`);
}
// Create a new item
createItem(item:
any): Observable<any> {
return
this.http.post<any>(this.apiUrl, item, {
headers: new
HttpHeaders({
'Content-Type': 'application/json'
})
});
}
// Update an
existing item
updateItem(id:
number, item: any): Observable<any> {
return
this.http.put<any>(`${this.apiUrl}/${id}`, item, {
headers: new
HttpHeaders({
'Content-Type': 'application/json'
})
});
}
// Delete an item
deleteItem(id:
number): Observable<any> {
return
this.http.delete<any>(`${this.apiUrl}/${id}`);
}
}
2. Create the Component for CRUD Operations
// crud.component.ts
import { Component, OnInit } from '@angular/core';
import { CrudService } from './crud.service';
@Component({
selector:
'app-crud',
templateUrl:
'./crud.component.html',
styleUrls:
['./crud.component.css']
})
export class CrudComponent implements OnInit {
items: any[] = [];
currentItem: any = {
id: 0, name: '' }; // Example object
constructor(private
crudService: CrudService) {}
ngOnInit(): void {
this.loadItems();
}
loadItems(): void {
this.crudService.getItems().subscribe(
(data: any[])
=> {
this.items =
data;
},
error => {
console.error('Error loading items', error);
}
);
}
createItem(): void {
this.crudService.createItem(this.currentItem).subscribe(
(data) => {
this.items.push(data); // Add the newly created item to the list
this.resetForm();
},
error => {
console.error('Error creating item', error);
}
);
}
updateItem(): void {
this.crudService.updateItem(this.currentItem.id,
this.currentItem).subscribe(
(data) => {
const index =
this.items.findIndex(item => item.id === data.id);
if (index !==
-1) {
this.items[index] = data; // Update the item in the list
}
this.resetForm();
},
error => {
console.error('Error
updating item', error);
}
);
}
deleteItem(id:
number): void {
this.crudService.deleteItem(id).subscribe(
() => {
this.items =
this.items.filter(item => item.id !== id); // Remove the item from the list
},
error => {
console.error('Error deleting item', error);
}
);
}
resetForm(): void {
this.currentItem =
{ id: 0, name: '' }; // Reset form fields
}
editItem(item: any):
void {
this.currentItem =
{ ...item }; // Set the form with the current item's data
}
}
3. Create the HTML Template for the Component
<!-- crud.component.html -->
<div>
<h2>CRUD
Operations</h2>
<!-- Form for
creating and editing items -->
<div>
<input
[(ngModel)]="currentItem.name" placeholder="Item Name"
/>
<button
(click)="currentItem.id ? updateItem() : createItem()">
{{
currentItem.id ? 'Update Item' : 'Create Item' }}
</button>
</div>
<!-- List of
items -->
<ul>
<li
*ngFor="let item of items">
{{ item.name }}
<button
(click)="editItem(item)">Edit</button>
<button
(click)="deleteItem(item.id)">Delete</button>
</li>
</ul>
</div>
4. Module Configuration
Ensure you have imported HttpClientModule and FormsModule
for HTTP requests and form handling.
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms'; // For ngModel
to work
import { AppComponent } from './app.component';
import { CrudComponent } from './crud/crud.component';
@NgModule({
declarations: [
AppComponent,
CrudComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule
],
providers: [],
bootstrap:
[AppComponent]
})
export class AppModule { }
5. Running the Application
Make sure your Angular project is running. In your terminal,
run:
ng serve
Notes:
- Backend:
This code assumes that your .NET Core API is configured to handle CRUD
operations at the specified endpoints.
- Error
Handling: It's a good practice to handle errors in your HTTP requests
and provide feedback to the user, but this example keeps it simple for
clarity.
- Styling:
Customize the styling and form as needed for your use case.
This Angular code sets up a simple interface to interact
with the .NET Core API. The CrudService handles the HTTP requests to perform
CRUD operations, and the component (CrudComponent) handles the user interface
for displaying, creating, updating, and deleting items.
Certainly! When using RxJS (Reactive Extensions for
JavaScript), we leverage operators to manage asynchronous streams of data more
effectively in Angular. The CRUD operations in Angular can be enhanced with
RxJS operators for handling HTTP requests, error handling, and more.
Below is an updated version of the Angular CRUD operations
using RxJS operators.
1. Create Angular Service with RxJS Operators
We'll use operators like map, catchError, switchMap, etc.,
to handle the responses and errors effectively.
// crud.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from
'@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class CrudService {
private apiUrl =
'https://your-api-url/api/items'; // Replace with your .NET Core API endpoint
constructor(private
http: HttpClient) { }
// Get all items
getItems():
Observable<any[]> {
return
this.http.get<any[]>(this.apiUrl).pipe(
map(data =>
data), // You can transform the data here if needed
catchError(this.handleError) // Handle errors here
);
}
// Get a single item
by ID
getItemById(id:
number): Observable<any> {
return
this.http.get<any>(`${this.apiUrl}/${id}`).pipe(
catchError(this.handleError) // Handle errors here
);
}
// Create a new item
createItem(item:
any): Observable<any> {
return
this.http.post<any>(this.apiUrl, item, {
headers: new
HttpHeaders({
'Content-Type': 'application/json'
})
}).pipe(
catchError(this.handleError) // Handle errors here
);
}
// Update an
existing item
updateItem(id:
number, item: any): Observable<any> {
return
this.http.put<any>(`${this.apiUrl}/${id}`, item, {
headers: new
HttpHeaders({
'Content-Type': 'application/json'
})
}).pipe(
catchError(this.handleError) // Handle errors here
);
}
// Delete an item
deleteItem(id:
number): Observable<any> {
return
this.http.delete<any>(`${this.apiUrl}/${id}`).pipe(
catchError(this.handleError) // Handle errors here
);
}
// Handle HTTP
errors
private
handleError(error: any) {
console.error('An
error occurred:', error);
return
throwError(() => new Error('Something went wrong; please try again
later.'));
}
}
2. Update Component with RxJS Operators
Now let's use RxJS operators like map, switchMap, and catchError
in the component to manage asynchronous data flow.
// crud.component.ts
import { Component, OnInit } from '@angular/core';
import { CrudService } from './crud.service';
import { catchError, of } from 'rxjs';
@Component({
selector:
'app-crud',
templateUrl:
'./crud.component.html',
styleUrls:
['./crud.component.css']
})
export class CrudComponent implements OnInit {
items: any[] = [];
currentItem: any = {
id: 0, name: '' }; // Example object
errorMessage: string
= '';
constructor(private
crudService: CrudService) {}
ngOnInit(): void {
this.loadItems();
}
loadItems(): void {
this.crudService.getItems().pipe(
catchError(err
=> {
this.errorMessage = 'Failed to load items';
return of([]);
// Return empty array on error
})
).subscribe(
(data: any[])
=> {
this.items =
data;
},
error => {
console.error('Error loading items', error);
}
);
}
createItem(): void {
this.crudService.createItem(this.currentItem).pipe(
catchError(err
=> {
this.errorMessage = 'Failed to create item';
return
of(null); // Return null on error
})
).subscribe(
(data) => {
if (data) {
this.items.push(data);
// Add the newly created item to the list
this.resetForm();
}
},
error => {
console.error('Error creating item', error);
}
);
}
updateItem(): void {
this.crudService.updateItem(this.currentItem.id, this.currentItem).pipe(
catchError(err
=> {
this.errorMessage = 'Failed to update item';
return
of(null); // Return null on error
})
).subscribe(
(data) => {
if (data) {
const index
= this.items.findIndex(item => item.id === data.id);
if (index
!== -1) {
this.items[index] = data; // Update the item in the list
}
this.resetForm();
}
},
error => {
console.error('Error
updating item', error);
}
);
}
deleteItem(id:
number): void {
this.crudService.deleteItem(id).pipe(
catchError(err
=> {
this.errorMessage = 'Failed to delete item';
return
of(null); // Return null on error
})
).subscribe(
() => {
this.items =
this.items.filter(item => item.id !== id); // Remove the item from the list
},
error => {
console.error('Error deleting item', error);
}
);
}
resetForm(): void {
this.currentItem =
{ id: 0, name: '' }; // Reset form fields
}
editItem(item: any):
void {
this.currentItem =
{ ...item }; // Set the form with the current item's data
}
}
3. HTML Template
Your HTML template remains the same as before, where it
handles the interaction with the user.
<!-- crud.component.html -->
<div>
<h2>CRUD
Operations</h2>
<!-- Error
Message -->
<div
*ngIf="errorMessage" class="error">{{ errorMessage
}}</div>
<!-- Form for
creating and editing items -->
<div>
<input
[(ngModel)]="currentItem.name" placeholder="Item Name"
/>
<button
(click)="currentItem.id ? updateItem() : createItem()">
{{
currentItem.id ? 'Update Item' : 'Create Item' }}
</button>
</div>
<!-- List of
items -->
<ul>
<li
*ngFor="let item of items">
{{ item.name }}
<button
(click)="editItem(item)">Edit</button>
<button
(click)="deleteItem(item.id)">Delete</button>
</li>
</ul>
</div>
Key Changes with RxJS:
- Error
Handling: We've used the catchError operator to catch any errors that
happen during the HTTP requests. If an error occurs, we display an
appropriate message, and also use of([]) or of(null) to return safe values
to continue the app flow.
- Observables
& Operators: RxJS operators like map and catchError are used to
manage data flow and errors. switchMap can be used in more complex
scenarios where one observable depends on another, but it’s not required
in this basic example.
- Async
Pipe: You can replace subscribe with the async pipe if you prefer
template-driven subscription management, though here we explicitly
subscribe in the component code.
Conclusion:
This example demonstrates how to perform CRUD operations
in an Angular 18 application using RxJS for asynchronous data
handling. RxJS allows us to manage side effects like errors and data
transformations effectively using operators like map, catchError, and switchMap.
To implement popups (modals) or toasts for
CRUD operations in Angular, you can integrate libraries such as Angular
Material for modals or use ngx-toastr for toasts. Here, I'll show
you how to implement both options.
1. Using Angular Material for Modals (Popups)
Let's first show how to create a popup (modal) to
confirm or display messages related to the CRUD operations.
Install Angular Material
First, make sure you have Angular Material installed:
ng add @angular/material
Then, choose a theme (e.g., Indigo/Pink), and add Animations
if prompted.
Next, import the necessary Angular Material modules into
your app.module.ts:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from
'@angular/platform-browser/animations';
import { MatDialogModule } from '@angular/material/dialog';
// Import MatDialog
import { MatButtonModule } from '@angular/material/button';
// Import MatButton
import { AppComponent } from './app.component';
import { CrudComponent } from './crud/crud.component';
import { DeleteConfirmDialogComponent } from
'./delete-confirm-dialog/delete-confirm-dialog.component'; // Add the dialog
component
@NgModule({
declarations: [
AppComponent,
CrudComponent,
DeleteConfirmDialogComponent // Declare the dialog component
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BrowserAnimationsModule,
MatDialogModule,
// Add to imports
MatButtonModule //
Add button module
],
providers: [],
bootstrap:
[AppComponent],
entryComponents:
[DeleteConfirmDialogComponent] // Declare the dialog component as entry
component
})
export class AppModule { }
Create a Confirmation Dialog for Deleting Items
Create a new component for the delete confirmation dialog:
ng generate component delete-confirm-dialog
Edit delete-confirm-dialog.component.ts to display a
confirmation message:
// delete-confirm-dialog.component.ts
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
@Component({
selector:
'app-delete-confirm-dialog',
templateUrl:
'./delete-confirm-dialog.component.html',
styleUrls:
['./delete-confirm-dialog.component.css']
})
export class DeleteConfirmDialogComponent {
constructor(public
dialogRef: MatDialogRef<DeleteConfirmDialogComponent>) {}
onConfirm(): void {
this.dialogRef.close(true); // Close the dialog and return true
}
onCancel(): void {
this.dialogRef.close(false); // Close the dialog and return false
}
}
Create the template for the dialog (delete-confirm-dialog.component.html):
<!-- delete-confirm-dialog.component.html -->
<h2 mat-dialog-title>Confirm Deletion</h2>
<mat-dialog-content>
Are you sure you
want to delete this item?
</mat-dialog-content>
<mat-dialog-actions>
<button
mat-button (click)="onCancel()">Cancel</button>
<button
mat-button color="warn"
(click)="onConfirm()">Delete</button>
</mat-dialog-actions>
Use the Dialog in CRUD Component
Now, in your CRUD Component, use the dialog to
confirm deletions:
// crud.component.ts
import { Component, OnInit } from '@angular/core';
import { CrudService } from './crud.service';
import { MatDialog } from '@angular/material/dialog';
import { DeleteConfirmDialogComponent } from
'./delete-confirm-dialog/delete-confirm-dialog.component';
import { catchError, of } from 'rxjs';
@Component({
selector:
'app-crud',
templateUrl:
'./crud.component.html',
styleUrls:
['./crud.component.css']
})
export class CrudComponent implements OnInit {
items: any[] = [];
currentItem: any = {
id: 0, name: '' }; // Example object
errorMessage: string
= '';
constructor(private
crudService: CrudService, private dialog: MatDialog) {}
ngOnInit(): void {
this.loadItems();
}
loadItems(): void {
this.crudService.getItems().pipe(
catchError(err
=> {
this.errorMessage = 'Failed to load items';
return of([]);
// Return empty array on error
})
).subscribe(
(data: any[])
=> {
this.items =
data;
},
error => {
console.error('Error loading items', error);
}
);
}
createItem(): void {
this.crudService.createItem(this.currentItem).pipe(
catchError(err
=> {
this.errorMessage = 'Failed to create item';
return
of(null); // Return null on error
})
).subscribe(
(data) => {
if (data) {
this.items.push(data); // Add the newly created item to the list
this.resetForm();
}
},
error => {
console.error('Error creating item', error);
}
);
}
updateItem(): void {
this.crudService.updateItem(this.currentItem.id,
this.currentItem).pipe(
catchError(err
=> {
this.errorMessage = 'Failed to update item';
return
of(null); // Return null on error
})
).subscribe(
(data) => {
if (data) {
const index
= this.items.findIndex(item => item.id === data.id);
if (index
!== -1) {
this.items[index] = data; // Update the item in the list
}
this.resetForm();
}
},
error => {
console.error('Error updating item', error);
}
);
}
deleteItem(id:
number): void {
const dialogRef =
this.dialog.open(DeleteConfirmDialogComponent);
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.crudService.deleteItem(id).pipe(
catchError(err => {
this.errorMessage = 'Failed to delete item';
return
of(null); // Return null on error
})
).subscribe(
() => {
this.items
= this.items.filter(item => item.id !== id); // Remove the item from the
list
},
error =>
{
console.error('Error deleting item', error);
}
);
}
});
}
resetForm(): void {
this.currentItem =
{ id: 0, name: '' }; // Reset form fields
}
editItem(item: any):
void {
this.currentItem =
{ ...item }; // Set the form with the current item's data
}
}
2. Using ngx-toastr for Toast Notifications
To show toast notifications for successful CRUD operations
or errors, you can use the ngx-toastr library.
Install ngx-toastr
npm install ngx-toastr
npm install @angular/animations
Import ngx-toastr Modules
Import the necessary modules into app.module.ts:
// app.module.ts
import { ToastrModule } from 'ngx-toastr';
import { BrowserAnimationsModule } from
'@angular/platform-browser/animations';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BrowserAnimationsModule,
ToastrModule.forRoot() // Add ToastrModule
]
})
export class AppModule { }
Using Toastr in CRUD Operations
Now, in the CRUD Component, you can inject and use ToastrService
to show success or error toasts.
// crud.component.ts
import { Component, OnInit } from '@angular/core';
import { CrudService } from './crud.service';
import { ToastrService } from 'ngx-toastr'; // Import
ToastrService
import { catchError, of } from 'rxjs';
@Component({
selector:
'app-crud',
templateUrl:
'./crud.component.html',
styleUrls:
['./crud.component.css']
})
export class CrudComponent implements OnInit {
items: any[] = [];
currentItem: any = {
id: 0, name: '' }; // Example object
errorMessage: string
= '';
constructor(
private
crudService: CrudService,
private toastr:
ToastrService // Inject ToastrService
) {}
ngOnInit(): void {
this.loadItems();
}
loadItems(): void {
this.crudService.getItems().pipe(
catchError(err
=> {
this.toastr.error('Failed to load items', 'Error'); // Toast error
return of([]);
// Return empty array on error
})
).subscribe(
(data: any[])
=> {
this.items =
data;
}
);
}
createItem(): void {
this.crudService.createItem(this.currentItem).pipe(
catchError(err
=> {
this.toastr.error('Failed to create item', 'Error'); // Toast error
return
of(null); // Return null on error
})
).subscribe(
(data) => {
if (data) {
this.items.push(data); // Add the newly created item to the list
this.resetForm();
this.toastr.success('Item created successfully', 'Success'); // Toast
success
}
}
);
}
updateItem(): void {
this.crudService.updateItem(this.currentItem.id, this.currentItem).pipe(
catchError(err
=> {
this.toastr.error('Failed to update item', 'Error'); // Toast error
return
of(null); // Return null on error
})
).subscribe(
(data) => {
if (data) {
const index
= this.items.findIndex(item => item.id === data.id);
if (index
!== -1) {
this.items[index] = data; // Update the item in the list
}
this.resetForm();
this.toastr.success('Item updated successfully', 'Success'); // Toast
success
}
}
);
}
deleteItem(id:
number): void {
const dialogRef =
this.dialog.open(DeleteConfirmDialogComponent);
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.crudService.deleteItem(id).pipe(
catchError(err => {
this.toastr.error('Failed to delete item', 'Error'); // Toast error
return
of(null); // Return null on error
})
).subscribe(
() => {
this.items
= this.items.filter(item => item.id !== id); // Remove the item from the
list
this.toastr.success('Item deleted successfully', 'Success'); // Toast
success
}
);
}
});
}
resetForm(): void {
this.currentItem =
{ id: 0, name: '' }; // Reset form fields
}
editItem(item: any):
void {
this.currentItem =
{ ...item }; // Set the form with the current item's data
}
}
Conclusion
You now have two options for handling feedback in your
Angular CRUD operations:
- Popups
(Modals): Use Angular Material to show a confirmation dialog for
delete operations.
- Toasts:
Use ngx-toastr to show success or error messages for the create,
update, and delete operations.
Both provide a user-friendly way to interact with the
application, offering visual feedback for each action.
To handle HTTP requests and responses globally in Angular,
you can use Http Interceptors. In your case, you want two interceptors:
- Global
Error Handling: To catch errors from all HTTP requests and show a
global error message.
- JWT
Token Handling: To attach a JWT token for authentication in HTTP
headers for two different services.
Here’s how you can implement these two interceptors.
1. Setting Up HTTP Interceptors in Angular
Step 1: Create the Global Error Handling Interceptor
First, create an interceptor for handling global errors.
ng generate service interceptors/global-error
Now, update the global-error.interceptor.ts file:
// global-error.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor,
HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { ToastrService } from 'ngx-toastr'; // Import Toastr
for error notifications
@Injectable()
export class GlobalErrorInterceptor implements
HttpInterceptor {
constructor(private
toastr: ToastrService) {}
intercept(req: any,
next: HttpHandler): Observable<HttpEvent<any>> {
return
next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
let
errorMessage = 'An unknown error occurred!';
if
(error.error instanceof ErrorEvent) {
//
Client-side error
errorMessage
= `Error: ${error.error.message}`;
} else {
//
Server-side error
if
(error.status === 0) {
errorMessage = 'No internet connection!';
} else if
(error.status === 400) {
errorMessage = 'Bad Request!';
} else if
(error.status === 401) {
errorMessage = 'Unauthorized! Please log in.';
} else if
(error.status === 500) {
errorMessage = 'Server error. Please try again later.';
}
}
// Show toastr
error message
this.toastr.error(errorMessage, 'Error');
return
throwError(() => new Error(errorMessage));
})
);
}
}
Step 2: Create the JWT Token Interceptor
Next, create an interceptor that adds a JWT token to HTTP
requests.
ng generate service interceptors/jwt-interceptor
Now, update the jwt-interceptor.ts file:
// jwt-interceptor.ts
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor,
HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from '../services/auth.service'; //
AuthService to get JWT token
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private
authService: AuthService) {}
intercept(req:
HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
const token =
this.authService.getToken(); // Assuming AuthService has getToken() method to
get the JWT
// If there is a
token, add it to the Authorization header
if (token) {
const
clonedRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return
next.handle(clonedRequest);
}
// If no token,
just forward the request
return
next.handle(req);
}
}
In the above code, the JwtInterceptor adds a JWT token to
the Authorization header of every HTTP request if the token exists.
Step 3: Update the AuthService
For the JWT interceptor to work, you need an AuthService
that manages the authentication token. Here’s an example of how the AuthService
might look.
// auth.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() { }
// Mock method to
get the JWT token (replace with real implementation)
getToken(): string |
null {
return
localStorage.getItem('auth_token'); // Replace with your token storage
mechanism
}
// Mock method to
set the JWT token (replace with real implementation)
setToken(token:
string): void {
localStorage.setItem('auth_token', token);
}
// Mock method to
clear the JWT token (replace with real implementation)
clearToken(): void {
localStorage.removeItem('auth_token');
}
}
This AuthService is where you'll store and retrieve the JWT
token (e.g., from localStorage or sessionStorage).
Step 4: Register the Interceptors in app.module.ts
Now that we’ve created the interceptors, we need to register
them in the Angular application.
Update app.module.ts to include the interceptors:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from
'@angular/common/http';
import { ToastrModule } from 'ngx-toastr'; // Import toastr
module
import { BrowserAnimationsModule } from
'@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { CrudComponent } from './crud/crud.component';
import { GlobalErrorInterceptor } from
'./interceptors/global-error.interceptor';
import { JwtInterceptor } from
'./interceptors/jwt-interceptor';
import { AuthService } from './services/auth.service';
@NgModule({
declarations: [
AppComponent,
CrudComponent
],
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
ToastrModule.forRoot()
// ToastrModule
],
providers: [
AuthService, //
Provide AuthService for JwtInterceptor
{
provide:
HTTP_INTERCEPTORS,
useClass:
JwtInterceptor,
multi: true //
Ensure multiple interceptors can be used
},
{
provide:
HTTP_INTERCEPTORS,
useClass:
GlobalErrorInterceptor,
multi: true //
Ensure multiple interceptors can be used
}
],
bootstrap:
[AppComponent]
})
export class AppModule { }
Here, we are adding both JwtInterceptor and GlobalErrorInterceptor
to the HTTP_INTERCEPTORS array in the providers section of the Angular module.
The multi: true option ensures that multiple interceptors can be chained.
2. Final Thoughts
- JWT
Interceptor: This interceptor will check for the JWT token in the AuthService,
and if it exists, it attaches it to the Authorization header of all
outgoing HTTP requests.
- Global
Error Interceptor: This interceptor will catch all errors in your HTTP
responses globally, and you can display error messages using Toastr or
handle them accordingly.
Now, your Angular app is set up with:
- Global
error handling using the GlobalErrorInterceptor.
- JWT
token handling using the JwtInterceptor.
Both interceptors will handle every HTTP request and
response automatically, allowing for centralized management of authentication
and error handling across your app.
In Angular, to pass values between components, we can use Subjects
from the RxJS library, which are a type of Observable that allows
both emitting values and subscribing to those values.
Here's how you can use Subjects to pass values from
one component to another component:
Steps to Pass Values Between Components Using Subjects:
- Create
a Service with a Subject: We'll use a shared service that contains a Subject
to store and emit the values.
- Send
Data from One Component: Use the service to emit data to the subject
when an event (like a button click) happens.
- Receive
Data in Another Component: The other component will subscribe to the
subject and receive the data.
1. Create a Shared Service with a Subject
First, create a service to manage the data passing.
ng generate service shared-data
Now, let's implement the service (shared-data.service.ts).
// shared-data.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SharedDataService {
// Create a subject
that will hold the data to be shared
private dataSubject
= new Subject<any>(); // You can
specify a type here
data$ =
this.dataSubject.asObservable(); // Create an observable from the subject
constructor() { }
// Method to send
data to other components
sendData(data: any):
void {
this.dataSubject.next(data); // Emit data to subscribers
}
}
- dataSubject:
A private Subject that will emit values.
- data$:
An observable that the components can subscribe to.
- sendData():
A method to send data using the next() method of the Subject.
2. Component 1: Sending Data
In this component, you will send the data to the service
when an action happens, such as a button click.
Let's assume this is ComponentA.
ng generate component componentA
Update component-a.component.ts:
// component-a.component.ts
import { Component } from '@angular/core';
import { SharedDataService } from '../shared-data.service';
// Import the shared service
@Component({
selector:
'app-component-a',
templateUrl:
'./component-a.component.html',
styleUrls:
['./component-a.component.css']
})
export class ComponentAComponent {
constructor(private
sharedDataService: SharedDataService) { }
// Method to send
data to ComponentB
sendDataToComponentB(): void {
const data = {
message: 'Hello from Component A!' }; //
Example data
this.sharedDataService.sendData(data); // Send data to the service
}
}
Now in component-a.component.html, you can trigger sending
data when a button is clicked:
<!-- component-a.component.html -->
<button
(click)="sendDataToComponentB()">Send Data to Component
B</button>
3. Component 2: Receiving Data
In this component, you will subscribe to the shared data
using the service.
Let's assume this is ComponentB.
ng generate component componentB
Update component-b.component.ts to subscribe to the data:
// component-b.component.ts
import { Component, OnInit, OnDestroy } from
'@angular/core';
import { SharedDataService } from '../shared-data.service';
// Import the shared service
import { Subscription } from 'rxjs';
@Component({
selector:
'app-component-b',
templateUrl:
'./component-b.component.html',
styleUrls:
['./component-b.component.css']
})
export class ComponentBComponent implements OnInit,
OnDestroy {
receivedData:
any; // Variable to hold the received
data
private
dataSubscription!: Subscription; // To
unsubscribe from the observable when the component is destroyed
constructor(private
sharedDataService: SharedDataService) { }
ngOnInit(): void {
// Subscribe to
the data observable from the service
this.dataSubscription = this.sharedDataService.data$.subscribe(data
=> {
this.receivedData = data; //
Assign received data to the local variable
});
}
ngOnDestroy(): void
{
// Unsubscribe to
prevent memory leaks
if
(this.dataSubscription) {
this.dataSubscription.unsubscribe();
}
}
}
In component-b.component.html, you can display the received
data:
<!-- component-b.component.html -->
<p>Received Data: {{ receivedData?.message
}}</p>
4. App Component (Optional)
You can place both ComponentA and ComponentB in the app.component.html
to visualize the interaction between the two components.
<!-- app.component.html -->
<app-component-a></app-component-a>
<app-component-b></app-component-b>
5. Final Thoughts
- SharedDataService:
This service contains a Subject that emits the data and exposes it as an
observable (data$). It allows other components to subscribe to the data
and also sends data from one component to another.
- Component
A: Sends data by calling the sendData() method in the SharedDataService
whenever an event occurs (like a button click).
- Component
B: Subscribes to the data$ observable and receives the data. When the
data is received, it can be used in the component, for example, to display
it.
- Unsubscribing:
It’s a good practice to unsubscribe from observables when a component is
destroyed to prevent memory leaks. This is done using the ngOnDestroy()
lifecycle hook in ComponentB.
Benefits of Using Subjects:
- Decoupling:
Components do not need to know about each other. They only depend on the
shared service, making the system more modular.
- Real-time
updates: Any component subscribing to the subject will receive updated
values immediately when new data is emitted.
This approach is great when you want to pass data
dynamically across components that are not directly related (parent-child
relationship).
Sure! To implement the functionality where you display a
list of data, and when you click or edit an item, it opens a form with two
dropdowns populated from the backend, here’s the Angular-only approach
to handle it.
I'll break down the solution into the following steps:
- Display
List of Data: We'll display a list of data in a table.
- Edit
Form: Clicking on an item will open a form for editing.
- Dropdowns
for Selection: The form will contain two dropdowns that will get their
options from the backend.
- Fetching
Data from Backend: Use Angular’s HTTP client to fetch data for the
dropdowns from the backend API.
Step 1: Setup Angular Service for HTTP Requests
Create a service to handle fetching data from the backend.
ng generate service data
In data.service.ts, implement the necessary HTTP calls to
get data.
// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private apiUrl =
'https://your-api-url.com/api'; //
Replace with your backend API URL
constructor(private
http: HttpClient) { }
// Fetch the list of
items (example data for display)
getItems():
Observable<any[]> {
return
this.http.get<any[]>(`${this.apiUrl}/items`);
}
// Fetch dropdown
data for first dropdown (Example: categories)
getDropdown1Data():
Observable<any[]> {
return
this.http.get<any[]>(`${this.apiUrl}/dropdown1`);
}
// Fetch dropdown
data for second dropdown (Example: brands)
getDropdown2Data():
Observable<any[]> {
return
this.http.get<any[]>(`${this.apiUrl}/dropdown2`);
}
// Save edited data
(optional, if you want to post the form data back to backend)
saveItem(data: any):
Observable<any> {
return
this.http.put<any>(`${this.apiUrl}/items/${data.id}`, data);
}
}
Step 2: Display List of Items with Edit Option
We will use a table to display the list of items, and each
item will have an "Edit" button that will open a form.
Create a component to display the list:
ng generate component item-list
In item-list.component.ts, fetch and display the list of
items:
// item-list.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; // For modal dialog to edit data (optional)
@Component({
selector:
'app-item-list',
templateUrl:
'./item-list.component.html',
styleUrls:
['./item-list.component.css']
})
export class ItemListComponent implements OnInit {
items: any[] =
[]; // List of items
selectedItem: any =
null; // Store selected item for editing
constructor(private
dataService: DataService, private modalService: NgbModal) { }
ngOnInit(): void {
this.loadItems(); // Load items
when component initializes
}
loadItems(): void {
this.dataService.getItems().subscribe(data
=> {
this.items =
data; // Store the fetched items in the
component variable
});
}
openEditModal(content: any, item: any): void {
this.selectedItem
= { ...item }; // Create a copy of the
item to be edited
this.modalService.open(content);
// Open the modal to edit
}
}
In item-list.component.html, display the list in a table
with an "Edit" button:
<!-- item-list.component.html -->
<div class="container">
<h2>Items
List</h2>
<table
class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let item of items">
<td>{{
item.id }}</td>
<td>{{
item.name }}</td>
<td><button class="btn btn-primary"
(click)="openEditModal(editModal,
item)">Edit</button></td>
</tr>
</tbody>
</table>
</div>
<!-- Modal for editing item -->
<ng-template #editModal let-modal>
<div
class="modal-header">
<h4
class="modal-title">Edit Item</h4>
<button
type="button" class="close
"
(click)="modal.dismiss()" aria-label="Close">
<span
aria-hidden="true">×</span>
</button>
</div>
<div
class="modal-body">
<form
(ngSubmit)="saveItem()">
<div
class="form-group">
<label
for="itemName">Item Name</label>
<input
type="text" id="itemName" class="form-control"
[(ngModel)]="selectedItem.name" name="name" required />
</div>
<!-- Dropdown
1 -->
<div
class="form-group">
<label
for="dropdown1">Select Category</label>
<select
id="dropdown1" class="form-control"
[(ngModel)]="selectedItem.category" name="category"
required>
<option
*ngFor="let option of dropdown1Data"
[value]="option.id">{{ option.name }}</option>
</select>
</div>
<!-- Dropdown
2 -->
<div
class="form-group">
<label
for="dropdown2">Select Brand</label>
<select
id="dropdown2" class="form-control"
[(ngModel)]="selectedItem.brand" name="brand" required>
<option
*ngFor="let option of dropdown2Data"
[value]="option.id">{{ option.name }}</option>
</select>
</div>
<button
type="submit" class="btn
btn-success">Save</button>
</form>
</div>
</ng-template>
Step 3: Handle Data Fetch for Dropdowns in Component
Next, in item-list.component.ts, we need to fetch the data
for the dropdowns when the modal opens.
// item-list.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector:
'app-item-list',
templateUrl:
'./item-list.component.html',
styleUrls:
['./item-list.component.css']
})
export class ItemListComponent implements OnInit {
items: any[] =
[]; // List of items
selectedItem: any =
null; // Store selected item for editing
dropdown1Data: any[]
= []; // Data for dropdown 1
dropdown2Data: any[]
= []; // Data for dropdown 2
constructor(private
dataService: DataService, private modalService: NgbModal) { }
ngOnInit(): void {
this.loadItems(); // Load items
when component initializes
this.loadDropdownData(); // Load
dropdown data when component initializes
}
loadItems(): void {
this.dataService.getItems().subscribe(data => {
this.items =
data; // Store the fetched items in the
component variable
});
}
loadDropdownData():
void {
this.dataService.getDropdown1Data().subscribe(data => {
this.dropdown1Data = data; //
Store the dropdown 1 data
});
this.dataService.getDropdown2Data().subscribe(data => {
this.dropdown2Data = data; //
Store the dropdown 2 data
});
}
openEditModal(content: any, item: any): void {
this.selectedItem
= { ...item }; // Create a copy of the
item to be edited
this.modalService.open(content);
// Open the modal to edit
}
saveItem(): void {
this.dataService.saveItem(this.selectedItem).subscribe(updatedItem =>
{
// Update the
item in the list with the updated item returned from the backend
const index =
this.items.findIndex(item => item.id === updatedItem.id);
if (index !==
-1) {
this.items[index] = updatedItem;
// Replace the updated item in the list
}
// Close the
modal
this.modalService.dismissAll();
});
}
}
Step 4: Using Bootstrap Modal for Form Popup
To open a modal with the form for editing, we use ng-bootstrap
(which is a Bootstrap library for Angular). If you don’t have it installed
already, install it by running:
npm install @ng-bootstrap/ng-bootstrap
Then, import the required modules in app.module.ts:
// app.module.ts
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
declarations: [
AppComponent,
ItemListComponent
],
imports: [
BrowserModule,
HttpClientModule,
NgbModule // Import NgbModule
],
providers: [],
bootstrap:
[AppComponent]
})
export class AppModule { }
Final Outcome
- Item
List: When you click the "Edit" button, it will open a modal
form to edit the item.
- Dropdowns:
The form contains two dropdowns populated with data fetched from the
backend using HTTP requests.
- Editing
and Saving: You can edit the item and save it back to the backend. The
updated data will be reflected in the list.
Notes:
- The
form uses ngModel for two-way data binding, so when the form is
edited, it updates the selectedItem in the component.
- The
NgbModal is used to show the modal with the form, which makes it
easy to handle popups in Angular.
- HTTP
requests are handled by the DataService using the Angular HTTP client.
This approach keeps the Angular code separate from the
backend logic and ensures that your frontend can interact with the backend API
seamlessly.
Comments
Post a Comment