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:

  1. Backend: This code assumes that your .NET Core API is configured to handle CRUD operations at the specified endpoints.
  2. 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.
  3. 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:

  1. 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.
  2. 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.
  3. 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:

  1. Global Error Handling: To catch errors from all HTTP requests and show a global error message.
  2. 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:

  1. Global error handling using the GlobalErrorInterceptor.
  2. 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:

  1. Create a Service with a Subject: We'll use a shared service that contains a Subject to store and emit the values.
  2. Send Data from One Component: Use the service to emit data to the subject when an event (like a button click) happens.
  3. 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:

  1. Display List of Data: We'll display a list of data in a table.
  2. Edit Form: Clicking on an item will open a form for editing.
  3. Dropdowns for Selection: The form will contain two dropdowns that will get their options from the backend.
  4. 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">&times;</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

Popular posts from this blog

Multiline to singleline IN C# - CODING

EF Core interview questions for beginners

EF Core interview questions for experienced