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.