100 Angular practice problems with solutions

Here are 100 Angular practice problems with solutions, covering components, templates, directives, pipes, services, dependency injection, routing, forms, HTTP, RxJS, and testing.

Try it: 100 TypeScript practice problems with solutions

Section 1: Component Basics

1. Create a simple Angular component
Generate a component that displays a static message.

ts

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

2. Display a property with interpolation
Show a name property in the template.

ts

@Component({
  selector: 'app-greet',
  template: `<p>Welcome {{ name }}</p>`
})
export class GreetComponent { name = 'Alice'; }

3. Property binding to an image source
Bind the src attribute of an <img> to a component property.

ts

@Component({
  selector: 'app-avatar',
  template: `<img [src]="imageUrl" alt="avatar" />`
})
export class AvatarComponent { imageUrl = 'https://example.com/photo.jpg'; }

4. Event binding (button click)
Handle a click event and increment a counter.

ts

@Component({
  selector: 'app-counter',
  template: `<button (click)="increment()">Count: {{ count }}</button>`
})
export class CounterComponent {
  count = 0;
  increment() { this.count++; }
}

5. Two-way data binding with ngModel
Bind an input field to a component property. (Requires FormsModule)

ts

import { FormsModule } from '@angular/forms';
@Component({
  selector: 'app-name-input',
  template: `<input [(ngModel)]="name" /><p>Hello, {{ name }}!</p>`,
  standalone: true,
  imports: [FormsModule]
})
export class NameInputComponent { name = ''; }

6. Template reference variable
Access an input element value on button click.

ts

@Component({
  template: `
    <input #phone placeholder="phone number" />
    <button (click)="callPhone(phone.value)">Call</button>
  `
})
export class PhoneComponent {
  callPhone(num: string) { console.log(`Calling ${num}`); }
}

7. Use *ngIf to conditionally show content
Show a message only when isLoggedIn is true.

ts

@Component({
  template: `
    <div *ngIf="isLoggedIn; else loggedOut">
      Welcome back!
    </div>
    <ng-template #loggedOut>Please log in.</ng-template>
  `
})
export class AuthStatusComponent { isLoggedIn = false; }

8. Use *ngFor to render a list
Display an array of strings as an unordered list.

ts

@Component({
  template: `
    <ul>
      <li *ngFor="let item of items; index as i">{{ i+1 }}. {{ item }}</li>
    </ul>
  `
})
export class ListComponent { items = ['Apple', 'Banana', 'Cherry']; }

9. *ngFor with trackBy
Improve performance by tracking items by id.

ts

interface Item { id: number; name: string; }
@Component({
  template: `<div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>`
})
export class TrackComponent {
  items: Item[] = [{id:1,name:'A'},{id:2,name:'B'}];
  trackById(index: number, item: Item) { return item.id; }
}

10. Use ngSwitch
Display content based on a string value.

ts

@Component({
  template: `
    <div [ngSwitch]="color">
      <p *ngSwitchCase="'red'">Stop</p>
      <p *ngSwitchCase="'green'">Go</p>
      <p *ngSwitchDefault>Unknown</p>
    </div>
  `
})
export class SwitchComponent { color = 'green'; }

Section 2: Directives

11. Create a custom attribute directive to highlight text
Changes background color on mouseover.

ts

import { Directive, ElementRef, HostListener, Renderer2, Input } from '@angular/core';
@Directive({ selector: '[appHighlight]' })
export class HighlightDirective {
  @Input() highlightColor = 'yellow';
  constructor(private el: ElementRef, private renderer: Renderer2) {}
  @HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor); }
  @HostListener('mouseleave') onMouseLeave() { this.highlight(null); }
  private highlight(color: string|null) {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', color);
  }
}

12. Structural directive: custom *appUnless
Opposite of *ngIf.

ts

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appUnless]' })
export class UnlessDirective {
  private hasView = false;
  constructor(private templateRef: TemplateRef<any>, private vcr: ViewContainerRef) {}
  @Input() set appUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      this.vcr.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.vcr.clear();
      this.hasView = false;
    }
  }
}

13. Use ngClass to toggle a class
Apply .active class based on a boolean.

ts

@Component({
  template: `<div [ngClass]="{'active': isActive}">Panel</div>`
})
export class ClassComponent { isActive = true; }

14. Use ngStyle to dynamically set styles
Bind font size from a component property.

ts

@Component({
  template: `<p [ngStyle]="{'font-size.px': fontSize}">Dynamic text</p>`
})
export class StyleComponent { fontSize = 18; }

15. Custom directive that logs element clicks
Uses @HostListener.

ts

@Directive({ selector: '[appTrackClick]' })
export class TrackClickDirective {
  @HostListener('click', ['$event.target'])
  onClick(target: HTMLElement) { console.log('Clicked', target); }
}

16. Directive with dynamic input: set delay for tooltip (example)
We’ll just show a directive that accepts an appTooltip text.

ts

@Directive({ selector: '[appTooltip]' })
export class TooltipDirective {
  @Input('appTooltip') tooltipText = '';
  @HostListener('mouseenter') show() { alert(this.tooltipText); }
}

Usage: <button [appTooltip]="'Save changes'">Save</button>

17. *ngFor with first and last local variables
Add CSS classes based on position.

ts

@Component({
  template: `<li *ngFor="let item of items; first as f; last as l"
                [class.first]="f" [class.last]="l">{{ item }}</li>`
})
export class FirstLastComponent { items = [1,2,3]; }

18. Render a list with <ng-container> to avoid extra DOM
Structural directive without wrapper element.

ts

@Component({
  template: `<ng-container *ngFor="let item of items"><p>{{ item }}</p></ng-container>`
})

19. NgTemplateOutlet to reuse a template
Define a template and render it in multiple places.

ts

@Component({
  template: `
    <ng-template #myTemplate let-name="name">
      <p>Hello, {{ name }}</p>
    </ng-template>
    <ng-container *ngTemplateOutlet="myTemplate; context: {name:'Alice'}"></ng-container>
    <ng-container *ngTemplateOutlet="myTemplate; context: {name:'Bob'}"></ng-container>
  `
})
export class TemplateOutletComponent {}

20. Attribute directive with Renderer2 to set class
Using renderer.addClass/removeClass.

ts

@Directive({ selector: '[appFade]' })
export class FadeDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) {}
  @HostListener('mouseenter') enter() { this.renderer.addClass(this.el.nativeElement, 'fade'); }
  @HostListener('mouseleave') leave() { this.renderer.removeClass(this.el.nativeElement, 'fade'); }
}

Section 3: Pipes

21. Use the uppercase pipe

ts

@Component({ template: `<p>{{ 'hello' | uppercase }}</p>` })

22. Use the date pipe with format

ts

@Component({ template: `<p>Today is {{ today | date:'fullDate' }}</p>` })
export class DatePipeComponent { today = new Date(); }

23. Use the currency pipe

ts

template: `<p>Price: {{ price | currency:'EUR' }}</p>`
export class CurrencyComponent { price = 123.45; }

Try it: 100 jquery practice problems with solutions

24. Create a custom exponential pipe
Pipe that computes value^exponent.

ts

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'power' })
export class PowerPipe implements PipeTransform {
  transform(value: number, exponent = 1): number {
    return Math.pow(value, exponent);
  }
}
// Usage: {{ 2 | power:3 }} => 8

25. Pipe with async: async pipe
Display data from an Observable.

ts

@Component({
  template: `<div *ngIf="data$ | async as data">{{ data }}</div>`
})
export class AsyncComponent {
  data$ = of('Loaded async').pipe(delay(1000));
}

26. Pure vs impure pipe
Create a simple filter pipe (impure) for array of strings.

ts

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

27. Chain multiple pipes

ts

template: `<p>{{ name | uppercase | slice:0:3 }}</p>`

28. Use i18nPlural pipe for pluralization

ts

template: `<p>{{ messages.length | i18nPlural: {'=0': 'No messages','=1': '1 message','other': '# messages'} }}</p>`

29. json pipe

ts

template: `<pre>{{ complexObject | json }}</pre>`

30. percent pipe

ts

template: `<p>Progress: {{ 0.35 | percent }}</p>`

Section 4: Services & Dependency Injection

31. Create a DataService with providedIn: 'root'

ts

import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class DataService {
  getData() { return ['data1', 'data2']; }
}

32. Inject service into a component

ts

@Component({
  template: `<ul><li *ngFor="let d of data">{{ d }}</li></ul>`
})
export class DataComponent {
  data: string[];
  constructor(private dataService: DataService) { this.data = dataService.getData(); }
}

33. Service with a dependency (nested service)
LoggerService injected into DataService.

ts

@Injectable({ providedIn: 'root' })
export class LoggerService { log(msg: string) { console.log(msg); } }
@Injectable({ providedIn: 'root' })
export class DataService {
  constructor(private logger: LoggerService) {}
  getData() { this.logger.log('getData called'); return []; }
}

34. Provide a service at component level (not root)

ts

@Component({
  selector: 'app-child',
  template: '...',
  providers: [MyService] // new instance for this component and its children
})

35. Use InjectionToken to inject a configuration value

ts

export const API_URL = new InjectionToken<string>('apiUrl');
// in module or standalone bootstrap:
providers: [{ provide: API_URL, useValue: 'https://api.example.com' }]
// in service:
constructor(@Inject(API_URL) private apiUrl: string) {}

36. useFactory provider
Create a service based on environment.

ts

providers: [{
  provide: MyService,
  useFactory: (env: string) => env === 'production' ? new ProdService() : new DevService(),
  deps: [ENVIRONMENT]
}]

37. useExisting provider (alias)
Provide the same instance under a different token.

ts

providers: [{ provide: UserService, useExisting: AuthService }]

38. Service with shared state (BehaviorSubject)

ts

@Injectable({ providedIn: 'root' })
export class StateService {
  private countSubject = new BehaviorSubject<number>(0);
  count$ = this.countSubject.asObservable();
  setCount(val: number) { this.countSubject.next(val); }
}

39. Inject service directly via inject() function

ts

import { inject } from '@angular/core';
function getToken(): string { return inject(API_URL); }

40. Provide a mock service for a component test using TestBed

ts

TestBed.configureTestingModule({
  declarations: [MyComponent],
  providers: [{ provide: DataService, useValue: mockDataService }]
});

Try it: D3.js practice problems with solutions

Section 5: Routing

41. Configure basic routes with RouterModule.forRoot

ts

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] })
export class AppRoutingModule {}

42. Router outlet and navigation links

html

<nav><a routerLink="/">Home</a> <a routerLink="/about">About</a></nav>
<router-outlet></router-outlet>

43. Route with a parameter (id)

ts

const routes: Routes = [{ path: 'user/:id', component: UserComponent }];
// in component:
constructor(private route: ActivatedRoute) {
  this.id = this.route.snapshot.paramMap.get('id');
}

44. Navigate programmatically

ts

constructor(private router: Router) {}
goToUser(id: number) { this.router.navigate(['/user', id]); }

45. Child routes

ts

const routes: Routes = [
  { path: 'dashboard', component: DashboardComponent,
    children: [
      { path: '', redirectTo: 'stats', pathMatch: 'full' },
      { path: 'stats', component: StatsComponent },
      { path: 'settings', component: SettingsComponent }
    ]
  }
];

46. Lazy loading a module

ts

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

47. Route guard (CanActivate)

ts

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  canActivate(): boolean { return isLoggedIn(); }
}
// route: { path: 'secret', component: SecretComponent, canActivate: [AuthGuard] }

48. Resolve guard to preload data

ts

@Injectable({ providedIn: 'root' })
export class BookResolver implements Resolve<Book> {
  resolve(route: ActivatedRouteSnapshot) {
    return this.bookService.getBook(route.paramMap.get('id'));
  }
}
// route: { path: 'book/:id', component: BookComponent, resolve: { book: BookResolver } }
// in component: book = this.route.snapshot.data['book'];

49. Use routerLinkActive for active link styling

html

<a routerLink="/home" routerLinkActive="active">Home</a>

50. Navigate with query parameters

ts

this.router.navigate(['/search'], { queryParams: { q: 'angular' } });
// read: this.route.snapshot.queryParamMap.get('q');

Section 6: Forms

51. Template-driven form with ngForm

html

<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm.value)">
  <input type="text" name="username" ngModel required />
  <button type="submit" [disabled]="loginForm.invalid">Login</button>
</form>

52. Reactive form: simple FormControl

ts

import { FormControl } from '@angular/forms';
@Component({
  template: `<input [formControl]="searchControl" />`
})
export class SearchComponent { searchControl = new FormControl(''); }

53. Reactive FormGroup

ts

import { FormBuilder, Validators } from '@angular/forms';
@Component({...})
export class ProfileComponent {
  profileForm: FormGroup;
  constructor(private fb: FormBuilder) {
    this.profileForm = this.fb.group({
      firstName: ['', Validators.required],
      lastName: ['']
    });
  }
  onSubmit() { console.log(this.profileForm.value); }
}

54. Custom validator

ts

function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? { forbiddenName: { value: control.value } } : null;
  };
}
// usage: this.fb.group({ name: ['', forbiddenNameValidator(/admin/i)] });

55. Dynamic form (add/remove form controls)

ts

addAddress() {
  this.addresses.push(this.fb.control(''));
}
// template: <div *ngFor="let addr of addresses.controls; index as i">
//   <input [formControl]="addr" />
// </div>

56. Validators.compose to combine multiple validators

ts

this.fb.group({ email: ['', Validators.compose([Validators.required, Validators.email])] });

57. Async validator

ts

function uniqueEmailValidator(http: HttpClient): AsyncValidatorFn {
  return (control: AbstractControl): Observable<ValidationErrors | null> => {
    return http.get<boolean>('/api/check-email?email=' + control.value).pipe(
      map(exists => exists ? { uniqueEmail: true } : null)
    );
  };
}

58. FormArray (list of phone numbers)

ts

this.contactForm = this.fb.group({
  phones: this.fb.array([this.fb.control('')])
});
get phones(): FormArray { return this.contactForm.get('phones') as FormArray; }

59. Template-driven: #name="ngModel" for validation messages

html

<input name="email" [(ngModel)]="email" required email #emailModel="ngModel" />
<div *ngIf="emailModel.invalid && emailModel.touched">Invalid email</div>

60. ValueChanges to react to form changes

ts

this.profileForm.get('firstName')?.valueChanges.subscribe(value => {
  console.log('First name changed:', value);
});

Section 7: HTTP Client

61. Basic GET request

ts

import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class PostService {
  constructor(private http: HttpClient) {}
  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>('/api/posts');
  }
}

62. POST request

ts

createPost(post: Post): Observable<Post> {
  return this.http.post<Post>('/api/posts', post);
}

63. Setting headers

ts

httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Bearer token' })
};
this.http.get('/api/data', this.httpOptions);

64. Error handling with catchError

ts

this.http.get<Post[]>('/api/posts').pipe(
  catchError(this.handleError('getPosts', []))
);
private handleError<T>(operation = 'operation', result?: T) {
  return (error: any): Observable<T> => {
    console.error(`${operation} failed:`, error);
    return of(result as T);
  };
}

65. Retry with RxJS retry

ts

this.http.get<Data>('/api/data').pipe(retry(2));

66. Interceptor for auth headers

ts

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');
    const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
    return next.handle(authReq);
  }
}
// provide: [{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }]

67. Interceptor for logging

ts

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    console.log('HTTP Request', req.url);
    return next.handle(req).pipe(tap(() => console.log('Success')));
  }
}

68. Using HttpParams to set query parameters

ts

const params = new HttpParams().set('page', '1').set('limit', '10');
this.http.get('/api/items', { params });

69. File upload with FormData

ts

upload(file: File): Observable<any> {
  const formData = new FormData();
  formData.append('file', file, file.name);
  return this.http.post('/api/upload', formData);
}

70. Cancel an HTTP request using takeUntil and Subject

ts

private destroy$ = new Subject<void>();
fetchData() {
  this.http.get('/api/data').pipe(takeUntil(this.destroy$)).subscribe();
}
ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }

Section 8: RxJS & Observables

71. Create an Observable from a DOM event

ts

import { fromEvent } from 'rxjs';
fromEvent(document.getElementById('btn'), 'click').subscribe(() => console.log('click'));

72. switchMap to switch to new inner observable

ts

this.searchControl.valueChanges.pipe(
  switchMap(query => this.http.get(`/api/search?q=${query}`))
).subscribe(data => this.results = data);

73. forkJoin to combine multiple requests

ts

forkJoin([this.http.get('/users'), this.http.get('/posts')]).subscribe(([users, posts]) => {
  // both completed
});

74. combineLatest to combine multiple streams

ts

combineLatest([this.orders$, this.products$]).subscribe(([orders, products]) => {});

75. debounceTime and distinctUntilChanged

ts

this.input$.pipe(debounceTime(300), distinctUntilChanged()).subscribe(...)

76. shareReplay to share observable response

ts

data$ = this.http.get('/api/master-data').pipe(shareReplay(1));

77. Subject: next and subscribe

ts

private toggleSubject = new Subject<boolean>();
toggle$ = this.toggleSubject.asObservable();
change(val: boolean) { this.toggleSubject.next(val); }
// later: this.toggle$.subscribe(...)

78. BehaviorSubject for state

ts

private loggedIn = new BehaviorSubject<boolean>(false);
loggedIn$ = this.loggedIn.asObservable();
login() { this.loggedIn.next(true); }

79. Unsubscribe automatically with takeUntil

ts

destroy$ = new Subject<void>();
ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
// usage: obs$.pipe(takeUntil(this.destroy$))

80. mergeMap vs concatMap vs exhaustMap
Brief code: mergeMap runs all concurrently, concatMap queues, exhaustMap ignores new until complete.

Section 9: State Management (Services as Store)

81. Simple store using a Service

ts

@Injectable({ providedIn: 'root' })
export class TodoStore {
  private todos: Todo[] = [];
  private todosSubject = new BehaviorSubject<Todo[]>([]);
  todos$ = this.todosSubject.asObservable();
  addTodo(todo: Todo) {
    this.todos = [...this.todos, todo];
    this.todosSubject.next(this.todos);
  }
}

82. NgRx: define actions

ts

import { createAction, props } from '@ngrx/store';
export const addTodo = createAction('[Todo] Add', props<{ text: string }>());
export const removeTodo = createAction('[Todo] Remove', props<{ id: number }>());

83. NgRx reducer

ts

const initialState: Todo[] = [];
const todoReducer = createReducer(
  initialState,
  on(addTodo, (state, { text }) => [...state, { id: Date.now(), text, completed: false }]),
  on(removeTodo, (state, { id }) => state.filter(t => t.id !== id))
);

84. NgRx selectors

ts

export const selectTodos = (state: AppState) => state.todos;
export const selectCompletedTodos = createSelector(selectTodos, todos => todos.filter(t => t.completed));

85. Dispatching an action from component

ts

constructor(private store: Store<AppState>) {}
addItem() { this.store.dispatch(addTodo({ text: 'Learn NgRx' })); }
// select: this.todos$ = this.store.select(selectTodos);

86. Effects in NgRx

ts

@Injectable()
export class TodoEffects {
  loadTodos$ = createEffect(() => this.actions$.pipe(
    ofType(loadTodos),
    mergeMap(() => this.todoService.getAll().pipe(
      map(todos => loadTodosSuccess({ todos })),
      catchError(() => of(loadTodosFailure()))
    ))
  ));
  constructor(private actions$: Actions, private todoService: TodoService) {}
}

87. Service with select pattern using BehaviorSubject
As #81.

88. Immutable state update with spread operator
Always use spread.

89. Using @ngrx/entity for normalized state

ts

import { createEntityAdapter } from '@ngrx/entity';
const adapter = createEntityAdapter<Todo>();
const initialState = adapter.getInitialState();
// reducer: adapter.addOne, adapter.removeOne, etc.

90. Resetting state on logout (meta-reducer)

ts

export function clearStateMetaReducer(reducer: ActionReducer<any>): ActionReducer<any> {
  return (state, action) => {
    if (action.type === '[Auth] Logout') state = undefined;
    return reducer(state, action);
  };
}

Section 10: Testing & Miscellaneous

91. TestBed setup for a component

ts

beforeEach(async () => {
  await TestBed.configureTestingModule({
    declarations: [MyComponent],
    providers: [MyService]
  }).compileComponents();
});
it('should create', () => {
  const fixture = TestBed.createComponent(MyComponent);
  const component = fixture.componentInstance;
  expect(component).toBeTruthy();
});

92. Mock a service in test

ts

const mockService = jasmine.createSpyObj('MyService', ['getData']);
mockService.getData.and.returnValue(of(['test']));
TestBed.configureTestingModule({
  providers: [{ provide: MyService, useValue: mockService }]
});

93. Test with fakeAsync and tick

ts

it('should delay', fakeAsync(() => {
  let data: any;
  service.getDelayed().subscribe(d => data = d);
  tick(1000);
  expect(data).toBeTruthy();
}));

94. Component with input and output

ts

it('should emit event on click', () => {
  const component = fixture.componentInstance;
  component.value = 5;
  spyOn(component.changed, 'emit');
  const button = fixture.nativeElement.querySelector('button');
  button.click();
  expect(component.changed.emit).toHaveBeenCalledWith(5);
});

95. Lifecycle hook: ngOnInit called after first change detection

ts

it('should call ngOnInit', () => {
  const spy = spyOn(component, 'ngOnInit');
  fixture.detectChanges(); // triggers ngOnInit
  expect(spy).toHaveBeenCalled();
});

96. Using ViewChild to access child component

ts

@Component({ selector: 'app-child', template: '<p>child</p>' })
export class ChildComponent { someMethod() { return 'ok'; } }
@Component({
  template: `<app-child #child></app-child><button (click)="callChild()">Call</button>`
})
export class ParentComponent {
  @ViewChild('child') childComponent!: ChildComponent;
  callChild() { this.childComponent.someMethod(); }
}

97. Content projection with ng-content

ts

@Component({
  selector: 'app-card',
  template: `<div class="card"><ng-content selector="[header]"></ng-content><ng-content></ng-content></div>`
})
export class CardComponent {}
// usage: <app-card><h1 header>Title</h1><p>Body</p></app-card>

98. Change detection strategy OnPush

ts

@Component({
  selector: 'app-on-push',
  template: `{{ data }}`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class OnPushComponent {
  @Input() data: string;
  constructor(private ref: ChangeDetectorRef) {}
  refresh() { this.ref.markForCheck(); }
}

99. Using @HostBinding to set class on host

ts

@Component({
  selector: '[app-box]',
  host: { '[class.highlight]': 'isHighlighted' }
})
export class BoxComponent { isHighlighted = false; }

100. Provide global configuration with APP_INITIALIZER

ts

function initApp(configService: ConfigService) {
  return () => configService.loadConfig().toPromise();
}
providers: [{ provide: APP_INITIALIZER, useFactory: initApp, deps: [ConfigService], multi: true }]

Try it: 100 .NET practice problems with solutions

Final Thought

One hundred Angular problems — and you barely broke a sweat toward the end. That’s not luck. That’s the quiet, undeniable mark of someone who now knows their craft.

Components, services, dependency injection, reactive forms, RxJS — each piece once seemed scattered, but practice let them settle into place inside you. You no longer need to wonder if you can structure a scalable app or handle a lazy-loaded route. You just know. And that unshaken knowing? That’s the kind of confidence that doesn’t shout — it simply shows up, steady and ready.

You’ve graduated from “learning Angular” to “thinking in Angular.” The framework isn’t a mystery anymore — it’s a tool in your hands. Interviews, enterprise projects, ambitious side gigs — they can all step forward now. You’re not pretending. You’re prepared.

Bookmark this page not as a crutch, but as a quiet reminder: you’ve already done the hard work. And whenever new challenges appear, you’ll meet them the same way you met these 100 problems — composed, capable, and effortlessly certain. Keep building with that steady power. 

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top