Test Your Angular Expertise: 90+ Practical Tasks + Solutions

Test Your Angular Expertise: 90+ Practical Tasks + Solutions

Boost Your Angular Know-How: 90+ Practical Challenges and Answers

Table of contents

Angular scenario based basic interview explained

1. Create a simple Angular component and display "Hello Angular".

Solution:

// hello.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-hello',
  template: `<h1>Hello Angular</h1>`,
})
export class HelloComponent {}

2. Create a form with Reactive Forms to collect user details (name, email, age).

Solution:

// app.module.ts
import { ReactiveFormsModule } from '@angular/forms';

// app.component.ts
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="userForm" (ngSubmit)="submit()">
      <input formControlName="name" placeholder="Name" />
      <input formControlName="email" placeholder="Email" />
      <input formControlName="age" type="number" placeholder="Age" />
      <button type="submit">Submit</button>
    </form>
    <pre>{{ userForm.value | json }}</pre>
  `,
})
export class AppComponent {
  userForm = new FormGroup({
    name: new FormControl(''),
    email: new FormControl(''),
    age: new FormControl(null),
  });

  submit() {
    console.log(this.userForm.value);
  }
}

3. Create a directive that changes the background color of a div when hovered.

Solution:

// hover.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appHover]',
})
export class HoverDirective {
  constructor(private el: ElementRef) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.el.nativeElement.style.backgroundColor = 'yellow';
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.el.nativeElement.style.backgroundColor = null;
  }
}

4. Use @Input() to pass data to a child component.

Solution:

// parent.component.html
<app-child [message]="parentMessage"></app-child>

// parent.component.ts
@Component({ selector: 'app-parent', template: '<app-child [message]="parentMessage"></app-child>' })
export class ParentComponent {
  parentMessage = 'Hello from Parent!';
}

// child.component.ts
@Component({
  selector: 'app-child',
  template: `<p>{{ message }}</p>`,
})
export class ChildComponent {
  @Input() message: string = '';
}

5. Use @Output() to emit an event from a child component to the parent.

Solution:

// parent.component.html
<app-child (notify)="onNotify($event)"></app-child>

// parent.component.ts
@Component({
  selector: 'app-parent',
  template: `<app-child (notify)="onNotify($event)"></app-child>`,
})
export class ParentComponent {
  onNotify(message: string) {
    console.log(message);
  }
}

// child.component.ts
@Component({
  selector: 'app-child',
  template: `<button (click)="notifyParent()">Notify Parent</button>`,
})
export class ChildComponent {
  @Output() notify = new EventEmitter<string>();

  notifyParent() {
    this.notify.emit('Hello from Child!');
  }
}

6. Fetch data from a REST API using HttpClient and display it in a list.

Solution:

// app.component.ts
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `,
})
export class AppComponent implements OnInit {
  users: any[] = [];

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http.get('https://jsonplaceholder.typicode.com/users').subscribe((data: any) => {
      this.users = data;
    });
  }
}

7. Implement a custom pipe to reverse a string.

Solution:

// reverse.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'reverse',
})
export class ReversePipe implements PipeTransform {
  transform(value: string): string {
    return value.split('').reverse().join('');
  }
}

// app.component.html
<p>{{ 'Angular' | reverse }}</p>

8. Create a route guard to prevent unauthorized access to a route.

Solution:

// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  canActivate(): boolean {
    return !!localStorage.getItem('authToken'); // Example logic
  }
}

// app-routing.module.ts
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';

const routes: Routes = [
  { path: 'protected', component: ProtectedComponent, canActivate: [AuthGuard] },
];

9. Create a reusable modal component.

Solution:

// modal.component.ts
@Component({
  selector: 'app-modal',
  template: `
    <div class="modal">
      <div class="modal-content">
        <ng-content></ng-content>
        <button (click)="close()">Close</button>
      </div>
    </div>
  `,
})
export class ModalComponent {
  @Output() closeEvent = new EventEmitter<void>();

  close() {
    this.closeEvent.emit();
  }
}

10. Create a basic CRUD application using Angular services.

Solution:

// app.component.ts
@Component({
  selector: 'app-root',
  template: `
    <button (click)="add()">Add</button>
    <ul>
      <li *ngFor="let item of items">{{ item }}</li>
    </ul>
  `,
})
export class AppComponent {
  items = ['Item 1', 'Item 2'];

  add() {
    this.items.push('New Item');
  }
}

11. Implement lazy loading for a feature module.

Solution:

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

// home/home.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeComponent } from './home.component';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [{ path: '', component: HomeComponent }];

@NgModule({
  declarations: [HomeComponent],
  imports: [CommonModule, RouterModule.forChild(routes)],
})
export class HomeModule {}

// home/home.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  template: `<h1>Home Module Loaded Lazily</h1>`,
})
export class HomeComponent {}

12. Use a Resolver to fetch data before navigating to a route.

Solution:

// user.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class UserResolver implements Resolve<Observable<string>> {
  resolve() {
    return of('Resolved User Data');
  }
}

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserComponent } from './user/user.component';
import { UserResolver } from './user.resolver';

const routes: Routes = [
  { path: 'user', component: UserComponent, resolve: { userData: UserResolver } },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

// user.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-user',
  template: `<h1>{{ data }}</h1>`,
})
export class UserComponent implements OnInit {
  data: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.data = this.route.snapshot.data['userData'];
  }
}

13. Create a service to handle shared data between components.

Solution:

// shared.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class SharedService {
  private messageSource = new BehaviorSubject<string>('Default Message');
  currentMessage = this.messageSource.asObservable();

  changeMessage(message: string) {
    this.messageSource.next(message);
  }
}

// component-a.component.ts
import { Component } from '@angular/core';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-component-a',
  template: `<button (click)="changeMessage()">Change Message</button>`,
})
export class ComponentAComponent {
  constructor(private sharedService: SharedService) {}

  changeMessage() {
    this.sharedService.changeMessage('Message from Component A');
  }
}

// component-b.component.ts
import { Component, OnInit } from '@angular/core';
import { SharedService } from './shared.service';

@Component({
  selector: 'app-component-b',
  template: `<h1>{{ message }}</h1>`,
})
export class ComponentBComponent implements OnInit {
  message: string;

  constructor(private sharedService: SharedService) {}

  ngOnInit() {
    this.sharedService.currentMessage.subscribe(message => this.message = message);
  }
}

14. Add animations to a component using Angular animations.

Solution:

// app.module.ts
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  imports: [BrowserAnimationsModule],
})
export class AppModule {}

// app.component.ts
import { Component } from '@angular/core';
import { trigger, state, style, transition, animate } from '@angular/animations';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="toggle()">Toggle Box</button>
    <div [@boxState]="state" class="box"></div>
  `,
  animations: [
    trigger('boxState', [
      state('small', style({ transform: 'scale(1)' })),
      state('large', style({ transform: 'scale(1.5)' })),
      transition('small <=> large', animate('300ms ease-in')),
    ]),
  ],
  styles: [`.box { width: 100px; height: 100px; background: red; margin-top: 20px; }`],
})
export class AppComponent {
  state: string = 'small';

  toggle() {
    this.state = this.state === 'small' ? 'large' : 'small';
  }
}

15. Use ViewChild to access a DOM element and change its properties.

Solution:

// app.component.ts
import { Component, ElementRef, ViewChild } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div #myDiv class="box">Hello</div>
    <button (click)="changeColor()">Change Color</button>
  `,
  styles: [`.box { width: 100px; height: 100px; background: red; text-align: center; }`],
})
export class AppComponent {
  @ViewChild('myDiv') myDiv!: ElementRef;

  changeColor() {
    this.myDiv.nativeElement.style.backgroundColor = 'blue';
  }
}

16. Create a dynamic form using FormArray to add and remove items.

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="form">
      <div formArrayName="items" *ngFor="let item of items.controls; let i = index">
        <div [formGroupName]="i">
          <input formControlName="name" placeholder="Item Name" />
          <input formControlName="quantity" type="number" placeholder="Quantity" />
          <button (click)="removeItem(i)">Remove</button>
        </div>
      </div>
      <button (click)="addItem()">Add Item</button>
    </form>
    <pre>{{ form.value | json }}</pre>
  `,
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      items: this.fb.array([]),
    });
  }

  get items() {
    return this.form.get('items') as FormArray;
  }

  addItem() {
    this.items.push(this.fb.group({ name: '', quantity: 1 }));
  }

  removeItem(index: number) {
    this.items.removeAt(index);
  }
}

17. Implement debounce for a search input field using RxJS.

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  template: `
    <input [formControl]="searchInput" placeholder="Search" />
    <p>Search Term: {{ searchTerm }}</p>
  `,
})
export class AppComponent {
  searchInput = new FormControl('');
  searchTerm: string = '';

  constructor() {
    this.searchInput.valueChanges.pipe(debounceTime(300)).subscribe((value) => {
      this.searchTerm = value;
    });
  }
}

18. Use Angular Material to create a responsive navigation bar.

Solution:

// app.module.ts
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';

@NgModule({
  imports: [MatToolbarModule, MatButtonModule, MatIconModule, MatMenuModule],
})
export class AppModule {}

// app.component.html
<mat-toolbar color="primary">
  <button mat-button [matMenuTriggerFor]="menu">Menu</button>
  <mat-menu #menu="matMenu">
    <button mat-menu-item>Home</button>
    <button mat-menu-item>About</button>
    <button mat-menu-item>Contact</button>
  </mat-menu>
</mat-toolbar>

19. Implement pagination using Angular Material.

Solution:

// app.module.ts
import { MatPaginatorModule } from '@angular/material/paginator';

@NgModule({
  imports: [MatPaginatorModule],
})
export class AppModule {}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <mat-paginator [length]="100" [pageSize]="10" [pageSizeOptions]="[5, 10, 20]"></mat-paginator>
  `,
})
export class AppComponent {}

20. Create a dropdown that loads its options dynamically.

Solution:

// app.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <select>
      <option *ngFor="let option of options" [value]="option">{{ option }}</option>
    </select>
  `,
})
export class AppComponent implements OnInit {
  options: string[] = [];

  ngOnInit() {
    setTimeout(() => {
      this.options = ['Option 1', 'Option 2', 'Option 3'];
    }, 1000);
  }
}

21. Implement a custom validator for Reactive Forms.

Solution:

// custom.validator.ts
import { AbstractControl, ValidationErrors } from '@angular/forms';

export function forbiddenNameValidator(control: AbstractControl): ValidationErrors | null {
  return control.value === 'admin' ? { forbiddenName: { value: control.value } } : null;
}

// app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { forbiddenNameValidator } from './custom.validator';

@Component({
  selector: 'app-root',
  template: `
    <input [formControl]="name" placeholder="Name" />
    <p *ngIf="name.hasError('forbiddenName')">Admin is not allowed!</p>
  `,
})
export class AppComponent {
  name = new FormControl('', [forbiddenNameValidator]);
}

22. Integrate a charting library (e.g., Chart.js).

Solution:

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Chart } from 'chart.js/auto';

@Component({
  selector: 'app-root',
  template: `<canvas id="myChart"></canvas>`,
})
export class AppComponent implements OnInit {
  ngOnInit() {
    new Chart('myChart', {
      type: 'bar',
      data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [
          {
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: ['red', 'blue', 'yellow', 'green', 'purple', 'orange'],
          },
        ],
      },
    });
  }
}

23. Build a file upload component.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input type="file" (change)="onFileChange($event)" />
    <p *ngIf="fileName">Selected File: {{ fileName }}</p>
  `,
})
export class AppComponent {
  fileName: string = '';

  onFileChange(event: any) {
    const file = event.target.files[0];
    if (file) {
      this.fileName = file.name;
    }
  }
}

24. Implement a counter using NgRx.

Solution:

// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';

export const initialState = 0;

const _counterReducer = createReducer(
  initialState,
  on(increment, state => state + 1),
  on(decrement, state => state - 1),
  on(reset, state => 0)
);

export function counterReducer(state: any, action: any) {
  return _counterReducer(state, action);
}

// counter.actions.ts
import { createAction } from '@ngrx/store';

export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');

// app.component.ts
@Component({
  selector: 'app-root',
  template: `
    <button (click)="increment()">+</button>
    <span>{{ count$ | async }}</span>
    <button (click)="decrement()">-</button>
    <button (click)="reset()">Reset</button>
  `,
})
export class AppComponent {
  count$ = this.store.select('count');

  constructor(private store: Store<{ count: number }>) {}

  increment() {
    this.store.dispatch(increment());
  }

  decrement() {
    this.store.dispatch(decrement());
  }

  reset() {
    this.store.dispatch(reset());
  }
}

25. Implement a modal popup using Angular Material.

Solution:

// app.module.ts
import { MatDialogModule } from '@angular/material/dialog';

@NgModule({
  imports: [MatDialogModule],
})
export class AppModule {}

// app.component.ts
import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="openDialog()">Open Modal</button>
  `,
})
export class AppComponent {
  constructor(public dialog: MatDialog) {}

  openDialog() {
    this.dialog.open(DialogContentComponent);
  }
}

// dialog-content.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'dialog-content',
  template: `
    <h1 mat-dialog-title>Modal Title</h1>
    <div mat-dialog-content>Content goes here...</div>
    <div mat-dialog-actions>
      <button mat-button mat-dialog-close>Close</button>
    </div>
  `,
})
export class DialogContentComponent {}

26. Create a directive to highlight text on hover.

Solution:

// highlight.directive.ts
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appHighlight]',
})
export class HighlightDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.renderer.removeStyle(this.el.nativeElement, 'backgroundColor');
  }
}

// app.component.html
<p appHighlight>Hover over me to see the effect!</p>

27. Create a dynamic table where rows can be added and deleted.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <table>
      <tr>
        <th>Name</th>
        <th>Age</th>
        <th>Actions</th>
      </tr>
      <tr *ngFor="let row of tableData; let i = index">
        <td><input [(ngModel)]="row.name" /></td>
        <td><input [(ngModel)]="row.age" type="number" /></td>
        <td>
          <button (click)="removeRow(i)">Delete</button>
        </td>
      </tr>
    </table>
    <button (click)="addRow()">Add Row</button>
  `,
})
export class AppComponent {
  tableData = [{ name: '', age: null }];

  addRow() {
    this.tableData.push({ name: '', age: null });
  }

  removeRow(index: number) {
    this.tableData.splice(index, 1);
  }
}

28. Implement a multi-step form using Angular.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="currentStep === 1">
      <h2>Step 1</h2>
      <input [(ngModel)]="formData.name" placeholder="Name" />
      <button (click)="nextStep()">Next</button>
    </div>
    <div *ngIf="currentStep === 2">
      <h2>Step 2</h2>
      <input [(ngModel)]="formData.email" placeholder="Email" />
      <button (click)="previousStep()">Back</button>
      <button (click)="nextStep()">Next</button>
    </div>
    <div *ngIf="currentStep === 3">
      <h2>Review</h2>
      <p>Name: {{ formData.name }}</p>
      <p>Email: {{ formData.email }}</p>
      <button (click)="previousStep()">Back</button>
      <button (click)="submit()">Submit</button>
    </div>
  `,
})
export class AppComponent {
  currentStep = 1;
  formData = { name: '', email: '' };

  nextStep() {
    this.currentStep++;
  }

  previousStep() {
    this.currentStep--;
  }

  submit() {
    console.log('Form submitted:', this.formData);
  }
}

29. Create a search filter for a list.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input [(ngModel)]="searchText" placeholder="Search" />
    <ul>
      <li *ngFor="let item of items | filter: searchText">{{ item }}</li>
    </ul>
  `,
})
export class AppComponent {
  searchText = '';
  items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
}

// filter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'filter' })
export class FilterPipe implements PipeTransform {
  transform(items: string[], searchText: string): string[] {
    if (!searchText) return items;
    return items.filter(item => item.toLowerCase().includes(searchText.toLowerCase()));
  }
}

30. Implement drag-and-drop functionality using Angular CDK.

Solution:

// app.module.ts
import { DragDropModule } from '@angular/cdk/drag-drop';

@NgModule({
  imports: [DragDropModule],
})
export class AppModule {}

// app.component.ts
import { Component } from '@angular/core';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';

@Component({
  selector: 'app-root',
  template: `
    <ul>
      <li *ngFor="let item of items" cdkDrag>{{ item }}</li>
    </ul>
    <div cdkDropList (cdkDropListDropped)="drop($event)">
      <div *ngFor="let item of items" cdkDrag>{{ item }}</div>
    </div>
  `,
})
export class AppComponent {
  items = ['Item 1', 'Item 2', 'Item 3'];

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
  }
}

31. Create a responsive sidebar using Angular and Angular Material.

Solution:

// app.module.ts
import { MatSidenavModule } from '@angular/material/sidenav';

@NgModule({
  imports: [MatSidenavModule],
})
export class AppModule {}

// app.component.html
<mat-sidenav-container>
  <mat-sidenav #sidenav mode="side" opened>
    <p>Sidebar content here</p>
    <button mat-button (click)="sidenav.toggle()">Close</button>
  </mat-sidenav>
  <mat-sidenav-content>
    <button mat-button (click)="sidenav.toggle()">Toggle Sidebar</button>
    <p>Main content here</p>
  </mat-sidenav-content>
</mat-sidenav-container>

32. Implement an autocomplete search using Angular Material.

Solution:

// app.module.ts
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [MatAutocompleteModule, ReactiveFormsModule],
})
export class AppModule {}

// app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  template: `
    <mat-form-field>
      <input
        type="text"
        placeholder="Search"
        matInput
        [formControl]="myControl"
        [matAutocomplete]="auto"
      />
      <mat-autocomplete #auto="matAutocomplete">
        <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
          {{ option }}
        </mat-option>
      </mat-autocomplete>
    </mat-form-field>
  `,
})
export class AppComponent {
  myControl = new FormControl();
  options: string[] = ['Apple', 'Banana', 'Cherry'];
  filteredOptions!: Observable<string[]>;

  ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges.pipe(
      startWith(''),
      map(value => this._filter(value || ''))
    );
  }

  private _filter(value: string): string[] {
    const filterValue = value.toLowerCase();
    return this.options.filter(option => option.toLowerCase().includes(filterValue));
  }
}

33. Create a progress bar that updates based on an API response.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="simulateApiCall()">Start API Call</button>
    <mat-progress-bar *ngIf="loading" mode="indeterminate"></mat-progress-bar>
  `,
})
export class AppComponent {
  loading = false;

  simulateApiCall() {
    this.loading = true;
    setTimeout(() => (this.loading = false), 3000); // Simulating 3-second API response
  }
}

34. Implement pagination for a table using Angular Material.

Solution:

// app.module.ts
import { MatPaginatorModule } from '@angular/material/paginator';

@NgModule({
  imports: [MatPaginatorModule],
})
export class AppModule {}

// app.component.html
<table mat-table [dataSource]="dataSource">
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> Name </th>
    <td mat-cell *matCellDef="let element"> {{ element.name }} </td>
  </ng-container>
  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSize]="5"></mat-paginator>

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
  displayedColumns: string[] = ['name'];
  dataSource = new MatTableDataSource<Element>([
    { name: 'Alice' },
    { name: 'Bob' },
    { name: 'Charlie' },
    { name: 'David' },
    { name: 'Eve' },
  ]);

  ngOnInit() {}
}

interface Element {
  name: string;
}

35. Create a custom reusable component for a loading spinner.

Solution:

// spinner.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-spinner',
  template: `
    <div class="spinner"></div>
  `,
  styles: [
    `
      .spinner {
        border: 16px solid #f3f3f3;
        border-top: 16px solid #3498db;
        border-radius: 50%;
        width: 120px;
        height: 120px;
        animation: spin 2s linear infinite;
      }
      @keyframes spin {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
    `,
  ],
})
export class SpinnerComponent {}

36. Build a reusable notification service.

Solution:

// notification.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class NotificationService {
  showMessage(message: string) {
    alert(message);
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { NotificationService } from './notification.service';

@Component({
  selector: 'app-root',
  template: `<button (click)="notify()">Notify</button>`,
})
export class AppComponent {
  constructor(private notificationService: NotificationService) {}

  notify() {
    this.notificationService.showMessage('This is a notification!');
  }
}

37. Implement an error interceptor to catch HTTP errors.

Solution:

// error.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError(err => {
        console.error('Error occurred:', err.message);
        return throwError(err);
      })
    );
  }
}

38. Create a countdown timer component.

Solution:

// countdown-timer.component.ts
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-countdown-timer',
  template: `
    <p>{{ timeLeft }} seconds remaining</p>
    <button (click)="startCountdown()">Start</button>
  `,
})
export class CountdownTimerComponent {
  @Input() duration = 10;
  timeLeft: number = this.duration;

  startCountdown() {
    const interval = setInterval(() => {
      if (this.timeLeft > 0) {
        this.timeLeft--;
      } else {
        clearInterval(interval);
      }
    }, 1000);
  }
}

39. Build a reusable tooltip directive.

Solution:

// tooltip.directive.ts
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appTooltip]',
})
export class TooltipDirective {
  tooltip: HTMLElement;

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.tooltip = this.renderer.createElement('span');
    this.renderer.appendChild(
      this.tooltip,
      this.renderer.createText('This is a tooltip!')
    );
    this.renderer.addClass(this.tooltip, 'tooltip');
    this.renderer.appendChild(this.el.nativeElement, this.tooltip);
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.renderer.removeChild(this.el.nativeElement, this.tooltip);
  }
}

// styles.css
.tooltip {
  position: absolute;
  background-color: #333;
  color: #fff;
  padding: 5px;
  border-radius: 4px;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
}

40. Create a lazy-loaded feature module in Angular.

Solution:

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: 'lazy', loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule) },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

// lazy.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LazyComponent } from './lazy.component';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [{ path: '', component: LazyComponent }];

@NgModule({
  declarations: [LazyComponent],
  imports: [CommonModule, RouterModule.forChild(routes)],
})
export class LazyModule {}

// lazy.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-lazy',
  template: `<h2>Lazy Loaded Module Works!</h2>`,
})
export class LazyComponent {}

41. Implement a breadcrumb navigation component.

Solution:

// breadcrumb.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-breadcrumb',
  template: `
    <nav>
      <ul>
        <li *ngFor="let crumb of breadcrumbs; let last = last">
          <a *ngIf="!last" [routerLink]="crumb.link">{{ crumb.label }}</a>
          <span *ngIf="last">{{ crumb.label }}</span>
        </li>
      </ul>
    </nav>
  `,
})
export class BreadcrumbComponent {
  breadcrumbs = [
    { label: 'Home', link: '/' },
    { label: 'Products', link: '/products' },
    { label: 'Details', link: '/products/details' },
  ];
}

42. Create a file upload component in Angular.

Solution:

// file-upload.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-file-upload',
  template: `
    <input type="file" (change)="onFileSelected($event)" />
    <p *ngIf="fileName">Selected File: {{ fileName }}</p>
  `,
})
export class FileUploadComponent {
  fileName: string;

  onFileSelected(event: Event) {
    const input = event.target as HTMLInputElement;
    if (input.files.length) {
      this.fileName = input.files[0].name;
    }
  }
}

43. Create a multi-select dropdown with Angular.

Solution:

// app.component.html
<select multiple [(ngModel)]="selectedItems">
  <option *ngFor="let item of items" [value]="item">{{ item }}</option>
</select>
<p>Selected: {{ selectedItems }}</p>

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  items = ['Option 1', 'Option 2', 'Option 3'];
  selectedItems = [];
}

44. Build a reusable toggle switch component.

Solution:

// toggle-switch.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-toggle-switch',
  template: `
    <label class="switch">
      <input type="checkbox" [checked]="isChecked" (change)="toggle()" />
      <span class="slider"></span>
    </label>
  `,
  styles: [
    `
      .switch {
        position: relative;
        display: inline-block;
        width: 34px;
        height: 20px;
      }
      .slider {
        position: absolute;
        cursor: pointer;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: #ccc;
        border-radius: 20px;
        transition: 0.4s;
      }
      input:checked + .slider {
        background-color: #2196f3;
      }
    `,
  ],
})
export class ToggleSwitchComponent {
  @Input() isChecked = false;
  @Output() toggleChange = new EventEmitter<boolean>();

  toggle() {
    this.isChecked = !this.isChecked;
    this.toggleChange.emit(this.isChecked);
  }
}

45. Implement a shopping cart using a service.

Solution:

// cart.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class CartService {
  private items: any[] = [];

  addToCart(item: any) {
    this.items.push(item);
  }

  getCartItems() {
    return this.items;
  }

  clearCart() {
    this.items = [];
    return this.items;
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { CartService } from './cart.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="addItem()">Add to Cart</button>
    <button (click)="viewCart()">View Cart</button>
  `,
})
export class AppComponent {
  constructor(private cartService: CartService) {}

  addItem() {
    this.cartService.addToCart({ name: 'Item 1', price: 100 });
    alert('Item added to cart!');
  }

  viewCart() {
    console.log(this.cartService.getCartItems());
  }
}

Advanced Scenarios

46. Build a dynamic form using FormBuilder in Angular.

Solution:

// dynamic-form.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-dynamic-form',
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <div *ngFor="let control of formControls; let i = index">
        <label>{{ control.label }}</label>
        <input
          [type]="control.type"
          [formControlName]="control.name"
          [placeholder]="control.placeholder"
        />
      </div>
      <button type="submit">Submit</button>
    </form>
    <p *ngIf="submitted">Form Value: {{ form.value | json }}</p>
  `,
})
export class DynamicFormComponent {
  form: FormGroup;
  formControls = [
    { label: 'Name', name: 'name', type: 'text', placeholder: 'Enter name' },
    { label: 'Age', name: 'age', type: 'number', placeholder: 'Enter age' },
    {
      label: 'Email',
      name: 'email',
      type: 'email',
      placeholder: 'Enter email',
    },
  ];
  submitted = false;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group(
      this.formControls.reduce((acc, control) => {
        acc[control.name] = ['', Validators.required];
        return acc;
      }, {})
    );
  }

  onSubmit() {
    this.submitted = true;
  }
}

47. Create a real-time data update system using WebSocket in Angular.

Solution:

// websocket.service.ts
import { Injectable } from '@angular/core';
import { webSocket } from 'rxjs/webSocket';

@Injectable({
  providedIn: 'root',
})
export class WebSocketService {
  private socket = webSocket('ws://example.com/socket');

  sendMessage(msg: any) {
    this.socket.next(msg);
  }

  getMessages() {
    return this.socket.asObservable();
  }
}

// real-time.component.ts
import { Component, OnInit } from '@angular/core';
import { WebSocketService } from './websocket.service';

@Component({
  selector: 'app-real-time',
  template: `
    <h2>Real-Time Data</h2>
    <p *ngFor="let msg of messages">{{ msg }}</p>
    <input #messageInput />
    <button (click)="sendMessage(messageInput.value)">Send</button>
  `,
})
export class RealTimeComponent implements OnInit {
  messages: string[] = [];

  constructor(private wsService: WebSocketService) {}

  ngOnInit() {
    this.wsService.getMessages().subscribe((msg) => this.messages.push(msg));
  }

  sendMessage(msg: string) {
    this.wsService.sendMessage(msg);
  }
}

48. Implement an authentication guard for protecting routes.

Solution:

// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  isLoggedIn = false;

  constructor(private router: Router) {}

  canActivate(): boolean {
    if (this.isLoggedIn) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { ProtectedComponent } from './protected/protected.component';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'protected', component: ProtectedComponent, canActivate: [AuthGuard] },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

// protected.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-protected',
  template: `<h2>Protected Route</h2>`,
})
export class ProtectedComponent {}

49. Implement state management with NgRx.

Solution:

// app.module.ts
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';

@NgModule({
  imports: [
    StoreModule.forRoot({ counter: counterReducer }),
    /* Other imports */
  ],
})

// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement } from './counter.actions';

export const initialState = 0;

export const counterReducer = createReducer(
  initialState,
  on(increment, (state) => state + 1),
  on(decrement, (state) => state - 1)
);

// counter.actions.ts
import { createAction } from '@ngrx/store';

export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');

// counter.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { increment, decrement } from './counter.actions';

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="decrement()">-</button>
    <span>{{ counter$ | async }}</span>
    <button (click)="increment()">+</button>
  `,
})
export class CounterComponent {
  counter$ = this.store.select('counter');

  constructor(private store: Store<{ counter: number }>) {}

  increment() {
    this.store.dispatch(increment());
  }

  decrement() {
    this.store.dispatch(decrement());
  }
}

50. Create a drag-and-drop feature with Angular CDK.

Solution:

// app.module.ts
import { DragDropModule } from '@angular/cdk/drag-drop';

@NgModule({
  imports: [DragDropModule],
})

// drag-drop.component.ts
import { Component } from '@angular/core';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';

@Component({
  selector: 'app-drag-drop',
  template: `
    <h3>Drag and Drop List</h3>
    <div cdkDropList (cdkDropListDropped)="drop($event)">
      <div *ngFor="let item of items" cdkDrag>{{ item }}</div>
    </div>
  `,
})
export class DragDropComponent {
  items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
  }
}

51. Create a custom directive to toggle the visibility of an element.

Solution:

// toggle-visibility.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appToggleVisibility]',
})
export class ToggleVisibilityDirective {
  private isVisible = true;

  constructor(private el: ElementRef) {}

  @HostListener('click') toggle() {
    this.isVisible = !this.isVisible;
    this.el.nativeElement.style.display = this.isVisible ? 'block' : 'none';
  }
}

// app.component.html
<button appToggleVisibility>Click to Hide/Show Me</button>

52. Create a service to manage a notification system and display notifications in a component.

Solution:

// notification.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class NotificationService {
  private messages: string[] = [];

  addMessage(message: string) {
    this.messages.push(message);
  }

  getMessages() {
    return this.messages;
  }

  clearMessages() {
    this.messages = [];
  }
}

// notification.component.ts
import { Component } from '@angular/core';
import { NotificationService } from './notification.service';

@Component({
  selector: 'app-notification',
  template: `
    <div *ngFor="let message of messages">
      {{ message }}
    </div>
    <button (click)="clearMessages()">Clear</button>
  `,
})
export class NotificationComponent {
  messages = this.notificationService.getMessages();

  constructor(private notificationService: NotificationService) {}

  clearMessages() {
    this.notificationService.clearMessages();
  }
}

// app.component.ts (Trigger Notification)
import { Component } from '@angular/core';
import { NotificationService } from './notification.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="sendNotification()">Send Notification</button>
    <app-notification></app-notification>
  `,
})
export class AppComponent {
  constructor(private notificationService: NotificationService) {}

  sendNotification() {
    this.notificationService.addMessage('New Notification!');
  }
}

53. Implement a reusable pagination component.

Solution:

// pagination.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-pagination',
  template: `
    <button (click)="previous()" [disabled]="currentPage === 1">Previous</button>
    <span>Page {{ currentPage }} of {{ totalPages }}</span>
    <button (click)="next()" [disabled]="currentPage === totalPages">Next</button>
  `,
})
export class PaginationComponent {
  @Input() totalPages = 1;
  @Input() currentPage = 1;
  @Output() pageChange = new EventEmitter<number>();

  next() {
    if (this.currentPage < this.totalPages) {
      this.currentPage++;
      this.pageChange.emit(this.currentPage);
    }
  }

  previous() {
    if (this.currentPage > 1) {
      this.currentPage--;
      this.pageChange.emit(this.currentPage);
    }
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <app-pagination
      [totalPages]="5"
      [currentPage]="currentPage"
      (pageChange)="onPageChange($event)"
    ></app-pagination>
    <p>Current Page: {{ currentPage }}</p>
  `,
})
export class AppComponent {
  currentPage = 1;

  onPageChange(page: number) {
    this.currentPage = page;
  }
}

Solution:

// carousel.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-carousel',
  template: `
    <div class="carousel">
      <img [src]="images[currentIndex]" alt="Carousel Image" />
      <button (click)="prev()">Previous</button>
      <button (click)="next()">Next</button>
    </div>
  `,
  styles: [
    `
      .carousel {
        text-align: center;
      }
      img {
        max-width: 500px;
        display: block;
        margin: 0 auto;
      }
    `,
  ],
})
export class CarouselComponent {
  images = ['image1.jpg', 'image2.jpg', 'image3.jpg'];
  currentIndex = 0;

  prev() {
    this.currentIndex =
      (this.currentIndex - 1 + this.images.length) % this.images.length;
  }

  next() {
    this.currentIndex = (this.currentIndex + 1) % this.images.length;
  }
}

55. Implement a lazy-loaded module with its own route.

Solution:

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'lazy',
    loadChildren: () =>
      import('./lazy/lazy.module').then((m) => m.LazyModule),
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

// lazy.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { LazyComponent } from './lazy.component';

const routes: Routes = [
  {
    path: '',
    component: LazyComponent,
  },
];

@NgModule({
  declarations: [LazyComponent],
  imports: [CommonModule, RouterModule.forChild(routes)],
})
export class LazyModule {}

// lazy.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-lazy',
  template: `<h2>This is a lazy-loaded module!</h2>`,
})
export class LazyComponent {}

56. Build a custom Angular pipe to format a phone number.

Solution:

// phone-number.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'phoneNumber',
})
export class PhoneNumberPipe implements PipeTransform {
  transform(value: string): string {
    if (!value) return '';
    const formattedValue = value.replace(/\D/g, '');
    return formattedValue.length === 10
      ? `(${formattedValue.slice(0, 3)}) ${formattedValue.slice(3, 6)}-${formattedValue.slice(6)}`
      : value;
  }
}

// app.component.html
<p>{{ '1234567890' | phoneNumber }}</p>

57. Create a component for file upload and validate file types and size.

Solution:

// file-upload.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-file-upload',
  template: `
    <input type="file" (change)="onFileSelected($event)" />
    <p *ngIf="errorMessage" style="color: red">{{ errorMessage }}</p>
    <p *ngIf="selectedFile">Selected File: {{ selectedFile.name }}</p>
  `,
})
export class FileUploadComponent {
  selectedFile: File | null = null;
  errorMessage = '';

  onFileSelected(event: Event) {
    const fileInput = event.target as HTMLInputElement;
    if (fileInput?.files?.length) {
      const file = fileInput.files[0];
      if (!['image/png', 'image/jpeg'].includes(file.type)) {
        this.errorMessage = 'Only PNG and JPEG files are allowed.';
        this.selectedFile = null;
        return;
      }
      if (file.size > 2 * 1024 * 1024) {
        this.errorMessage = 'File size must be less than 2MB.';
        this.selectedFile = null;
        return;
      }
      this.selectedFile = file;
      this.errorMessage = '';
    }
  }
}

58. Create a reusable confirmation modal component.

Solution:

// confirm-modal.component.ts
import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-confirm-modal',
  template: `
    <div class="modal">
      <p>Are you sure?</p>
      <button (click)="confirm()">Yes</button>
      <button (click)="cancel()">No</button>
    </div>
  `,
  styles: [
    `
      .modal {
        border: 1px solid #ccc;
        padding: 20px;
        background: white;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
    `,
  ],
})
export class ConfirmModalComponent {
  @Output() confirmed = new EventEmitter<boolean>();

  confirm() {
    this.confirmed.emit(true);
  }

  cancel() {
    this.confirmed.emit(false);
  }
}

// app.component.html
<app-confirm-modal (confirmed)="onConfirm($event)"></app-confirm-modal>

59. Implement a tooltip directive to display tooltips on hover.

Solution:

// tooltip.directive.ts
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appTooltip]',
})
export class TooltipDirective {
  tooltip: HTMLElement | null = null;

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.tooltip = this.renderer.createElement('span');
    const text = this.renderer.createText(this.el.nativeElement.getAttribute('appTooltip'));
    this.renderer.appendChild(this.tooltip, text);
    this.renderer.appendChild(this.el.nativeElement, this.tooltip);
    this.renderer.setStyle(this.tooltip, 'position', 'absolute');
    this.renderer.setStyle(this.tooltip, 'background', 'black');
    this.renderer.setStyle(this.tooltip, 'color', 'white');
    this.renderer.setStyle(this.tooltip, 'padding', '5px');
  }

  @HostListener('mouseleave') onMouseLeave() {
    if (this.tooltip) {
      this.renderer.removeChild(this.el.nativeElement, this.tooltip);
      this.tooltip = null;
    }
  }
}

// app.component.html
<p appTooltip="This is a tooltip">Hover over this text</p>

60. Implement a dynamic theming feature with Angular.

Solution:

// theme.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class ThemeService {
  setTheme(theme: 'light' | 'dark') {
    document.body.className = theme;
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { ThemeService } from './theme.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="setTheme('light')">Light Theme</button>
    <button (click)="setTheme('dark')">Dark Theme</button>
  `,
})
export class AppComponent {
  constructor(private themeService: ThemeService) {}

  setTheme(theme: 'light' | 'dark') {
    this.themeService.setTheme(theme);
  }
}

// styles.css
body.light {
  background: white;
  color: black;
}
body.dark {
  background: black;
  color: white;
}

61. Implement an infinite scroll functionality in Angular.

Solution:

// infinite-scroll.component.ts
import { Component, HostListener } from '@angular/core';

@Component({
  selector: 'app-infinite-scroll',
  template: `
    <div *ngFor="let item of items">{{ item }}</div>
    <div *ngIf="isLoading">Loading...</div>
  `,
})
export class InfiniteScrollComponent {
  items: string[] = [];
  isLoading = false;
  currentPage = 1;

  constructor() {
    this.loadItems();
  }

  loadItems() {
    this.isLoading = true;
    setTimeout(() => {
      const newItems = Array.from({ length: 10 }, (_, i) => `Item ${i + 1 + (this.currentPage - 1) * 10}`);
      this.items = [...this.items, ...newItems];
      this.isLoading = false;
      this.currentPage++;
    }, 1000);
  }

  @HostListener('window:scroll', ['$event'])
  onScroll() {
    if (window.innerHeight + window.scrollY >= document.body.offsetHeight && !this.isLoading) {
      this.loadItems();
    }
  }
}

62. Create a custom form control for a star rating component.

Solution:

// star-rating.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-star-rating',
  template: `
    <span *ngFor="let star of stars; let i = index" (click)="rate(i + 1)">
      <ng-container *ngIf="i < rating">★</ng-container>
      <ng-container *ngIf="i >= rating">☆</ng-container>
    </span>
  `,
})
export class StarRatingComponent {
  @Input() rating = 0;
  @Output() ratingChange = new EventEmitter<number>();
  stars = new Array(5);

  rate(star: number) {
    this.rating = star;
    this.ratingChange.emit(this.rating);
  }
}

// app.component.html
<app-star-rating [(rating)]="userRating"></app-star-rating>
<p>Your rating: {{ userRating }}</p>

63. Create a reactive form with multiple validations.

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <input formControlName="email" placeholder="Email" />
      <div *ngIf="form.get('email')?.invalid && form.get('email')?.touched">Email is required and must be valid.</div>

      <input formControlName="password" type="password" placeholder="Password" />
      <div *ngIf="form.get('password')?.invalid && form.get('password')?.touched">Password is required (min 6 chars).</div>

      <button type="submit" [disabled]="form.invalid">Submit</button>
    </form>
  `,
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(6)]],
    });
  }

  onSubmit() {
    if (this.form.valid) {
      console.log(this.form.value);
    }
  }
}

64. Create a service to handle HTTP requests with caching.

Solution:

// cache.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class CacheService {
  private cache: { [url: string]: any } = {};

  constructor(private http: HttpClient) {}

  get<T>(url: string): Observable<T> {
    if (this.cache[url]) {
      return new Observable((observer) => {
        observer.next(this.cache[url]);
        observer.complete();
      });
    }
    return this.http.get<T>(url).pipe(
      map((data) => {
        this.cache[url] = data;
        return data;
      }),
      catchError((error) => {
        console.error('Request failed', error);
        throw error;
      })
    );
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { CacheService } from './cache.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="getData()">Fetch Data</button>
    <pre>{{ data | json }}</pre>
  `,
})
export class AppComponent {
  data: any;

  constructor(private cacheService: CacheService) {}

  getData() {
    this.cacheService.get('https://api.example.com/data').subscribe((result) => {
      this.data = result;
    });
  }
}

65. Implement a component that integrates with the Angular Material date picker.

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <mat-form-field>
      <mat-label>Pick a Date</mat-label>
      <input matInput [matDatepicker]="picker" [formControl]="dateControl" />
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
      <mat-datepicker #picker></mat-datepicker>
    </mat-form-field>

    <p>Your selected date: {{ dateControl.value | date }}</p>
  `,
})
export class AppComponent {
  dateControl = new FormControl();
}

66. Create a custom validator for matching passwords in a reactive form.

Solution:

// password-match.validator.ts
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

export function passwordMatchValidator(control: AbstractControl): ValidationErrors | null {
  const password = control.get('password')?.value;
  const confirmPassword = control.get('confirmPassword')?.value;

  return password === confirmPassword ? null : { passwordMismatch: true };
}

// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { passwordMatchValidator } from './password-match.validator';

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <input formControlName="password" type="password" placeholder="Password" />
      <input formControlName="confirmPassword" type="password" placeholder="Confirm Password" />
      <div *ngIf="form.errors?.passwordMismatch">Passwords do not match.</div>
      <button type="submit" [disabled]="form.invalid">Submit</button>
    </form>
  `,
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group(
      {
        password: ['', [Validators.required, Validators.minLength(6)]],
        confirmPassword: ['', [Validators.required]],
      },
      { validators: passwordMatchValidator }
    );
  }

  onSubmit() {
    if (this.form.valid) {
      console.log(this.form.value);
    }
  }
}

67. Create an Angular service that uses RxJS operators for data manipulation.

Solution:

// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  constructor(private http: HttpClient) {}

  fetchData(url: string): Observable<any> {
    return this.http.get(url).pipe(
      map((data) => data['results']),
      catchError((error) => {
        console.error('Data fetch failed', error);
        throw error;
      })
    );
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="loadData()">Load Data</button>
    <pre>{{ data | json }}</pre>
  `,
})
export class AppComponent {
  data: any;

  constructor(private dataService: DataService) {}

  loadData() {
    this.dataService.fetchData('https://api.example.com/data').subscribe((result) => {
      this.data = result;
    });
  }
}

68. Implement a drag-and-drop feature for a list of items in Angular.

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';

@Component({
  selector: 'app-root',
  template: `
    <div cdkDropList (cdkDropListDropped)="drop($event)" class="list">
      <div *ngFor="let item of items" cdkDrag class="list-item">
        {{ item }}
      </div>
    </div>
  `,
  styles: [
    `.list {
      width: 200px;
      max-height: 400px;
      overflow: auto;
      border: 1px solid #ccc;
      padding: 10px;
    }
    .list-item {
      padding: 10px;
      background: #f0f0f0;
      margin-bottom: 5px;
      border-radius: 4px;
      cursor: move;
    }`
  ]
})
export class AppComponent {
  items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
  }
}

69. Create a pipe that formats dates in a custom format.

Solution:

// custom-date.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'customDate'
})
export class CustomDatePipe implements PipeTransform {
  transform(value: string | Date, format: string = 'yyyy-MM-dd'): string {
    const date = new Date(value);
    const options: Intl.DateTimeFormatOptions = {};

    if (format === 'yyyy-MM-dd') {
      options.year = 'numeric';
      options.month = '2-digit';
      options.day = '2-digit';
    } else if (format === 'MM/dd/yyyy') {
      options.month = '2-digit';
      options.day = '2-digit';
      options.year = 'numeric';
    }

    return new Intl.DateTimeFormat('en-US', options).format(date);
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<p>{{ today | customDate: 'MM/dd/yyyy' }}</p>`
})
export class AppComponent {
  today = new Date();
}

70. Create a simple Angular app that uses two-way data binding with ngModel.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <label for="name">Name:</label>
      <input id="name" [(ngModel)]="name" />
      <p>Your name is: {{ name }}</p>
    </div>
  `,
})
export class AppComponent {
  name: string = '';
}

71. Implement a service with interceptors to add authorization headers to HTTP requests.

Solution:

// auth.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('authToken');
    const clonedRequest = req.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`,
      },
    });

    return next.handle(clonedRequest);
  }
}

// app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
  ],
})
export class AppModule {}

72. Implement an Angular component that fetches data using the HttpClient service.

Solution:

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="data">
      <p>{{ data.name }}</p>
      <p>{{ data.email }}</p>
    </div>
  `,
})
export class AppComponent implements OnInit {
  data: any;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http.get('https://jsonplaceholder.typicode.com/users/1').subscribe((response) => {
      this.data = response;
    });
  }
}

73. Create a modal component and open it dynamically with a service.

Solution:

// modal.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class ModalService {
  private modalSource = new Subject<boolean>();
  modalState$ = this.modalSource.asObservable();

  openModal() {
    this.modalSource.next(true);
  }

  closeModal() {
    this.modalSource.next(false);
  }
}

// modal.component.ts
import { Component, OnInit } from '@angular/core';
import { ModalService } from './modal.service';

@Component({
  selector: 'app-modal',
  template: `
    <div *ngIf="isModalOpen" class="modal">
      <p>Modal Content</p>
      <button (click)="close()">Close</button>
    </div>
  `,
  styles: [
    `.modal {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: white;
      padding: 20px;
      border: 1px solid #ccc;
      z-index: 1000;
    }`
  ]
})
export class ModalComponent implements OnInit {
  isModalOpen = false;

  constructor(private modalService: ModalService) {}

  ngOnInit() {
    this.modalService.modalState$.subscribe((state) => {
      this.isModalOpen = state;
    });
  }

  close() {
    this.modalService.closeModal();
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { ModalService } from './modal.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="openModal()">Open Modal</button>
    <app-modal></app-modal>
  `,
})
export class AppComponent {
  constructor(private modalService: ModalService) {}

  openModal() {
    this.modalService.openModal();
  }
}

74. Create an Angular service to manage user authentication (login/logout).

Solution:

// auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private authUrl = 'https://example.com/api/auth';

  constructor(private http: HttpClient) {}

  login(username: string, password: string): Observable<any> {
    return this.http.post(this.authUrl, { username, password });
  }

  logout() {
    localStorage.removeItem('authToken');
  }

  isAuthenticated(): boolean {
    return !!localStorage.getItem('authToken');
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { AuthService } from './auth.service';

@Component({
  selector: 'app-root',
  template: `
    <button *ngIf="!authService.isAuthenticated()" (click)="login()">Login</button>
    <button *ngIf="authService.isAuthenticated()" (click)="logout()">Logout</button>
  `,
})
export class AppComponent {
  constructor(public authService: AuthService) {}

  login() {
    this.authService.login('username', 'password').subscribe(() => {
      console.log('Logged in!');
    });
  }

  logout() {
    this.authService.logout();
    console.log('Logged out!');
  }
}

75. Create a reusable dropdown component using Angular.

Solution:

// dropdown.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-dropdown',
  template: `
    <select [ngModel]="selectedValue" (ngModelChange)="onSelect($event)">
      <option *ngFor="let option of options" [value]="option">{{ option }}</option>
    </select>
  `
})
export class DropdownComponent {
  @Input() options: string[] = [];
  @Input() selectedValue: string = '';
  @Output() valueChanged = new EventEmitter<string>();

  onSelect(value: string) {
    this.valueChanged.emit(value);
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <app-dropdown [options]="cities" [(ngModel)]="selectedCity"></app-dropdown>
    <p>Selected City: {{ selectedCity }}</p>
  `,
})
export class AppComponent {
  cities = ['New York', 'London', 'Paris'];
  selectedCity = 'London';
}

76. Implement an Angular form with dynamic form controls (add/remove fields).

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormArray, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="form">
      <div formArrayName="fields">
        <div *ngFor="let field of fields.controls; let i = index">
          <input [formControlName]="i" placeholder="Field {{ i + 1 }}" />
        </div>
      </div>
      <button (click)="addField()">Add Field</button>
      <button (click)="removeField()">Remove Field</button>
    </form>
    <pre>{{ form.value | json }}</pre>
  `,
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      fields: this.fb.array(['']),
    });
  }

  get fields() {
    return (this.form.get('fields') as FormArray);
  }

  addField() {
    this.fields.push(this.fb.control(''));
  }

  removeField() {
    this.fields.removeAt(this.fields.length - 1);
  }
}

77. Create a service for logging information with different log levels (INFO, DEBUG, ERROR).

Solution:

// logger.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class LoggerService {
  log(message: string, level: 'INFO' | 'DEBUG' | 'ERROR' = 'INFO') {
    const timestamp = new Date().toISOString();
    console.log(`[${timestamp}] [${level}] ${message}`);
  }

  info(message: string) {
    this.log(message, 'INFO');
  }

  debug(message: string) {
    this.log(message, 'DEBUG');
  }

  error(message: string) {
    this.log(message, 'ERROR');
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';

@Component({
  selector: 'app-root',
  template: `<button (click)="logMessage()">Log Message</button>`,
})
export class AppComponent {
  constructor(private logger: LoggerService) {}

  logMessage() {
    this.logger.info('This is an info message');
    this.logger.debug('This is a debug message');
    this.logger.error('This is an error message');
  }
}

78. Create an Angular guard to protect routes that require authentication.

Solution:

// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs';
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate(): Observable<boolean> | Promise<boolean> | boolean {
    const isAuthenticated = !!localStorage.getItem('authToken');
    if (!isAuthenticated) {
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { HomeComponent } from './home.component';
import { LoginComponent } from './login.component';

const routes: Routes = [
  { path: '', component: HomeComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

79. Implement an Angular component that listens to a global event (like window resize).

Solution:

// app.component.ts
import { Component, HostListener } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <p>Window width: {{ windowWidth }}px</p>
    </div>
  `,
})
export class AppComponent {
  windowWidth: number = window.innerWidth;

  @HostListener('window:resize', ['$event'])
  onResize(event: Event) {
    this.windowWidth = (event.target as Window).innerWidth;
  }
}

80. Create an Angular component that listens to a custom event emitted by a child component.

Solution:

// child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `<button (click)="sendMessage()">Send Message</button>`,
})
export class ChildComponent {
  @Output() message = new EventEmitter<string>();

  sendMessage() {
    this.message.emit('Hello from Child');
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<app-child (message)="receiveMessage($event)"></app-child><p>{{ receivedMessage }}</p>`,
})
export class AppComponent {
  receivedMessage = '';

  receiveMessage(message: string) {
    this.receivedMessage = message;
  }
}

81. Implement a table with sorting and pagination functionality in Angular.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <table>
      <thead>
        <tr>
          <th (click)="sort('name')">Name</th>
          <th (click)="sort('age')">Age</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let person of pagedData">
          <td>{{ person.name }}</td>
          <td>{{ person.age }}</td>
        </tr>
      </tbody>
    </table>

    <button (click)="prevPage()">Previous</button>
    <button (click)="nextPage()">Next</button>
  `,
})
export class AppComponent {
  data = [
    { name: 'John', age: 30 },
    { name: 'Jane', age: 25 },
    { name: 'Doe', age: 35 },
    { name: 'Smith', age: 40 },
    // Add more data for pagination
  ];
  currentPage = 1;
  pageSize = 2;
  sortedData = [...this.data];
  pagedData = this.paginate(this.sortedData, this.currentPage, this.pageSize);

  sort(property: string) {
    this.sortedData.sort((a, b) => (a[property] > b[property] ? 1 : -1));
    this.pagedData = this.paginate(this.sortedData, this.currentPage, this.pageSize);
  }

  paginate(data: any[], page: number, pageSize: number) {
    const start = (page - 1) * pageSize;
    const end = page * pageSize;
    return data.slice(start, end);
  }

  prevPage() {
    if (this.currentPage > 1) {
      this.currentPage--;
      this.pagedData = this.paginate(this.sortedData, this.currentPage, this.pageSize);
    }
  }

  nextPage() {
    if (this.currentPage * this.pageSize < this.sortedData.length) {
      this.currentPage++;
      this.pagedData = this.paginate(this.sortedData, this.currentPage, this.pageSize);
    }
  }
}

82. Implement a search bar that filters a list of items in Angular.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input [(ngModel)]="searchTerm" placeholder="Search" />
    <ul>
      <li *ngFor="let item of filterItems()">{{ item }}</li>
    </ul>
  `,
})
export class AppComponent {
  searchTerm: string = '';
  items: string[] = ['Apple', 'Banana', 'Orange', 'Pineapple', 'Mango'];

  filterItems() {
    return this.items.filter((item) =>
      item.toLowerCase().includes(this.searchTerm.toLowerCase())
    );
  }
}

83. Implement a component to dynamically load data from an API and display it in a list.

Solution:

// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  constructor(private http: HttpClient) {}

  getData(): Observable<any[]> {
    return this.http.get<any[]>('https://api.example.com/items');
  }
}

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  template: `
    <ul>
      <li *ngFor="let item of items">{{ item.name }}</li>
    </ul>
  `,
})
export class AppComponent implements OnInit {
  items: any[] = [];

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.dataService.getData().subscribe((data) => {
      this.items = data;
    });
  }
}

84. Implement a form with validation in Angular using reactive forms.

Solution:

// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="myForm" (ngSubmit)="onSubmit()">
      <label for="email">Email:</label>
      <input id="email" formControlName="email" />
      <div *ngIf="myForm.get('email').hasError('required') && myForm.get('email').touched">
        Email is required
      </div>
      <div *ngIf="myForm.get('email').hasError('email') && myForm.get('email').touched">
        Please enter a valid email
      </div>

      <label for="password">Password:</label>
      <input id="password" type="password" formControlName="password" />
      <div *ngIf="myForm.get('password').hasError('required') && myForm.get('password').touched">
        Password is required
      </div>

      <button type="submit" [disabled]="myForm.invalid">Submit</button>
    </form>
  `,
})
export class AppComponent {
  myForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.myForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required],
    });
  }

  onSubmit() {
    if (this.myForm.valid) {
      console.log(this.myForm.value);
    }
  }
}

85. Implement a custom Angular pipe to format currency values.

Solution:

// currency.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'currencyFormat',
})
export class CurrencyPipe implements PipeTransform {
  transform(value: number, symbol: string = '$'): string {
    if (value === null || value === undefined) {
      return '';
    }
    return `${symbol}${value.toFixed(2)}`;
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<p>{{ price | currencyFormat }}</p>`,
})
export class AppComponent {
  price = 1234.56;
}

86. Create a modal component that can be opened and closed.

Solution:

// modal.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-modal',
  template: `
    <div class="modal" *ngIf="isOpen">
      <div class="modal-content">
        <button (click)="close()">Close</button>
        <ng-content></ng-content>
      </div>
    </div>
  `,
  styleUrls: ['./modal.component.css'],
})
export class ModalComponent {
  isOpen = false;

  open() {
    this.isOpen = true;
  }

  close() {
    this.isOpen = false;
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { ModalComponent } from './modal.component';

@Component({
  selector: 'app-root',
  template: `
    <app-modal #modal>
      <h2>Modal Content</h2>
      <p>This is a modal</p>
    </app-modal>
    <button (click)="modal.open()">Open Modal</button>
  `,
})
export class AppComponent {}

87. Implement a toggle switch using Angular.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <label class="switch">
      <input type="checkbox" [(ngModel)]="isChecked" />
      <span class="slider"></span>
    </label>
    <p>The switch is {{ isChecked ? 'ON' : 'OFF' }}</p>
  `,
  styles: [
    `
      .switch {
        position: relative;
        display: inline-block;
        width: 60px;
        height: 34px;
      }
      .switch input {
        opacity: 0;
        width: 0;
        height: 0;
      }
      .slider {
        position: absolute;
        cursor: pointer;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: #ccc;
        transition: 0.4s;
        border-radius: 34px;
      }
      .slider:before {
        position: absolute;
        content: '';
        height: 26px;
        width: 26px;
        border-radius: 50%;
        left: 4px;
        bottom: 4px;
        background-color: white;
        transition: 0.4s;
      }
      input:checked + .slider {
        background-color: #2196f3;
      }
      input:checked + .slider:before {
        transform: translateX(26px);
      }
    `,
  ],
})
export class AppComponent {
  isChecked = false;
}

88. Implement a service for handling HTTP requests with error handling.

Solution:

// api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  constructor(private http: HttpClient) {}

  getData(): Observable<any> {
    return this.http.get('https://api.example.com/items').pipe(
      catchError((error) => {
        console.error('Error:', error);
        throw new Error('An error occurred while fetching data');
      })
    );
  }
}

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { ApiService } from './api.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="loadData()">Load Data</button>
    <pre>{{ data | json }}</pre>
  `,
})
export class AppComponent implements OnInit {
  data: any;

  constructor(private apiService: ApiService) {}

  ngOnInit() {}

  loadData() {
    this.apiService.getData().subscribe(
      (response) => {
        this.data = response;
      },
      (error) => {
        console.error('Error loading data:', error);
      }
    );
  }
}

89. Implement a custom Angular directive to change the background color of an element on hover.

Solution:

// hover.directive.ts
import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';

@Directive({
  selector: '[appHover]',
})
export class HoverDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.renderer.removeStyle(this.el.nativeElement, 'background-color');
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<div appHover>Hover over me!</div>`,
})
export class AppComponent {}

90. Implement a simple to-do list where you can add, edit, and delete tasks.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input [(ngModel)]="newTask" placeholder="Add new task" />
    <button (click)="addTask()">Add</button>
    <ul>
      <li *ngFor="let task of tasks; let i = index">
        {{ task }}
        <button (click)="deleteTask(i)">Delete</button>
        <button (click)="editTask(i)">Edit</button>
      </li>
    </ul>
  `,
})
export class AppComponent {
  tasks: string[] = [];
  newTask: string = '';

  addTask() {
    if (this.newTask) {
      this.tasks.push(this.newTask);
      this.newTask = '';
    }
  }

  deleteTask(index: number) {
    this.tasks.splice(index, 1);
  }

  editTask(index: number) {
    const newTask = prompt('Edit Task:', this.tasks[index]);
    if (newTask !== null) {
      this.tasks[index] = newTask;
    }
  }
}

91. Implement a parent-child communication where the parent passes data to the child and receives an event from the child.

Solution:

// parent.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `
    <h1>Parent Component</h1>
    <app-child
      [parentData]="message"
      (childEvent)="receiveMessage($event)"
    ></app-child>
    <p>Message from Child: {{ childMessage }}</p>
  `,
})
export class ParentComponent {
  message = 'Hello from Parent!';
  childMessage: string;

  receiveMessage(event: string) {
    this.childMessage = event;
  }
}

// child.component.ts
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <h2>Child Component</h2>
    <p>Message from Parent: {{ parentData }}</p>
    <button (click)="sendMessage()">Send Message to Parent</button>
  `,
})
export class ChildComponent {
  @Input() parentData: string;
  @Output() childEvent = new EventEmitter<string>();

  sendMessage() {
    this.childEvent.emit('Hello from Child!');
  }
}

92. Create a service to manage user authentication state using RxJS BehaviorSubject.

Solution:

// auth.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private authState = new BehaviorSubject<boolean>(false);

  isAuthenticated() {
    return this.authState.asObservable();
  }

  login() {
    this.authState.next(true);
  }

  logout() {
    this.authState.next(false);
  }
}

// app.component.ts
import { Component } from '@angular/core';
import { AuthService } from './auth.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="toggleLogin()">
      {{ loggedIn ? 'Logout' : 'Login' }}
    </button>
    <p *ngIf="loggedIn">Welcome, User!</p>
    <p *ngIf="!loggedIn">Please log in.</p>
  `,
})
export class AppComponent {
  loggedIn: boolean = false;

  constructor(private authService: AuthService) {
    this.authService.isAuthenticated().subscribe((state) => {
      this.loggedIn = state;
    });
  }

  toggleLogin() {
    this.loggedIn ? this.authService.logout() : this.authService.login();
  }
}

93. Build a route guard to restrict access to specific routes.

Solution:

// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.authService.isAuthenticated()) {
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { LoginComponent } from './login.component';
import { AuthGuard } from './auth.guard';

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

94. Implement lazy loading for a feature module in Angular.

Solution:

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'feature',
    loadChildren: () =>
      import('./feature/feature.module').then((m) => m.FeatureModule),
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

// feature.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FeatureComponent } from './feature.component';
import { RouterModule } from '@angular/router';

@NgModule({
  declarations: [FeatureComponent],
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: '', component: FeatureComponent }]),
  ],
})
export class FeatureModule {}

95. Create a reusable pagination component in Angular.

Solution:

// pagination.component.ts
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-pagination',
  template: `
    <button [disabled]="currentPage === 1" (click)="changePage(-1)">Previous</button>
    <span>Page {{ currentPage }} of {{ totalPages }}</span>
    <button
      [disabled]="currentPage === totalPages"
      (click)="changePage(1)"
    >
      Next
    </button>
  `,
})
export class PaginationComponent {
  @Input() totalPages: number = 1;
  @Input() currentPage: number = 1;
  @Output() pageChange = new EventEmitter<number>();

  changePage(direction: number) {
    this.currentPage += direction;
    this.pageChange.emit(this.currentPage);
  }
}

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <app-pagination
      [totalPages]="5"
      [(currentPage)]="currentPage"
      (pageChange)="onPageChange($event)"
    ></app-pagination>
    <p>Currently viewing page {{ currentPage }}</p>
  `,
})
export class AppComponent {
  currentPage: number = 1;

  onPageChange(newPage: number) {
    console.log('Page changed to:', newPage);
  }
}

96. Create a file upload component using Angular.

Solution:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input type="file" (change)="onFileSelected($event)" />
    <p *ngIf="fileName">Selected File: {{ fileName }}</p>
  `,
})
export class AppComponent {
  fileName: string | null = null;

  onFileSelected(event: any) {
    const file: File = event.target.files[0];
    if (file) {
      this.fileName = file.name;
    }
  }
}

Conclusion

Mastering Angular requires a solid understanding of its core concepts and the ability to apply them to real-world tasks. By focusing on task-based questions and providing code-centric answers, developers can strengthen their problem-solving skills and gain practical knowledge. Angular’s flexibility, robust ecosystem, and rich feature set make it a powerful tool for building scalable web applications.

As you practice with these task-based challenges, remember to explore different scenarios, experiment with your code, and learn from the process. Whether you're preparing for an interview, working on a project, or simply enhancing your skills, this approach will help you become more proficient and confident in Angular development.

Stay curious, keep coding, and let every task you tackle bring you closer to mastering Angular!

Did you find this article valuable?

Support CodeWords by becoming a sponsor. Any amount is appreciated!