Angular JS Interview questions: Coding Challenges

Master Angular with 100 challenges. Every card contains a clear problem statement, a detailed technical explanation, and the exact code solution.

Beginner Level (Core & Templates)

Q01
Implement one-way data binding.
Beginner
Problem: You need to display a dynamic string variable from the component class in the HTML template.
Details: Angular uses double curly braces {{ }} for interpolation. This is one-way data binding from the component class to the template, ensuring the DOM updates when the class property changes.
@Component({
  template: '<h1>Hello, {{ name }}!</h1>',
})
export class UserComponent {
  name = 'Alice';
}
Q02
Implement property binding.
Beginner
Problem: You need to dynamically disable a button based on a boolean state variable.
Details: Use square brackets [property] to bind a DOM element’s property to a component class variable. Unlike interpolation, property binding safely sets boolean states on DOM nodes (like disabled).
@Component({
  template: '<button [disabled]="isProcessing">Submit</button>'
})
export class SubmitComponent {
  isProcessing = true;
}
Q03
Implement event binding.
Beginner
Problem: You need to execute a component method when a user clicks a button.
Details: Use parentheses (event) to bind a DOM event to a method in your class. This handles user interactions like clicks, keystrokes, and form submissions.
@Component({
  template: '<button (click)="onClick()">Click Me</button>'
})
export class ClickComponent {
  onClick() { console.log('Clicked!'); }
}
Q04
Implement two-way data binding.
Beginner
Problem: You need an input field to update a variable in real-time, and changing the variable should update the input field.
Details: Use the “banana-in-a-box” syntax [(ngModel)]. This combines property binding and event binding. You must import FormsModule to use it.
// Requires: import { FormsModule } from '@angular/forms';
@Component({
  imports: [FormsModule],
  template: '<input [(ngModel)]="user" /> <p>{{ user }}</p>'
})
export class InputComponent {
  user = '';
}
Q05
Use modern control flow `@if`.
Beginner
Problem: Conditionally render an HTML element based on a boolean value.
Details: Angular v17 introduced the built-in @if block, replacing *ngIf. It provides better performance, cleaner syntax, and doesn’t require importing CommonModule.
@Component({
  template: `
    @if (isVisible) { <div>Visible</div> } 
    @else { <div>Hidden</div> }
  `
})
export class ToggleComponent {
  isVisible = true;
}
Q06
Use modern control flow `@for`.
Beginner
Problem: Loop over an array of items and render an <li> for each.
Details: Angular v17 introduced @for, replacing *ngFor. It mandates a track expression for performance, completely eliminating the need for a separate trackBy function.
@Component({
  template: `
    <ul>
      @for (item of items; track item.id) {
        <li>{{ item.name }}</li>
      }
    </ul>
  `
})
export class ListComponent {
  items = [{ id: 1, name: 'Apple' }];
}
Q07
Use the `@empty` block.
Beginner
Problem: Show a fallback “No items” message when an array is empty during a loop.
Details: The new @for block seamlessly integrates an @empty block that renders automatically if the provided array has a length of zero.
@Component({
  template: `
    @for (item of items; track item.id) {
      <div>{{ item.name }}</div>
    } @empty {
      <div>No items found.</div>
    }
  `
})
Q08
Pass data to a Child Component.
Beginner
Problem: A parent component needs to pass a configuration string into a child component.
Details: Decorate a property in the child component with @Input(). The parent can then use property binding [propName] to pass data in.
// Child
export class ChildComp { @Input() config!: string; }

// Parent Template
<app-child [config]="'Dark Mode'"></app-child>
Q09
Emit an event to a Parent Component.
Beginner
Problem: A child component needs to notify its parent when an action occurs.
Details: Use @Output() paired with an EventEmitter. The child calls .emit(data), and the parent listens using standard event binding (eventName).
// Child
export class ChildComp {
  @Output() action = new EventEmitter<string>();
  trigger() { this.action.emit('Done!'); }
}

// Parent Template
<app-child (action)="handleAction($event)"></app-child>
Q10
Apply a CSS class dynamically.
Beginner
Problem: Toggle an ‘active’ CSS class on a div based on a component property.
Details: Use class binding [class.className]="condition". It adds the class if the condition is truthy and removes it if falsy.
@Component({
  template: '<div [class.active]="isActive">Status</div>'
})
export class StyleComp { isActive = true; }
Q11
Apply inline styles dynamically.
Beginner
Problem: Change text color to red or green depending on an error state.
Details: Use style binding [style.property]="value" for single styles, or [ngStyle] for multiple dynamically evaluated styles.
@Component({
  template: '<div [style.color]="isErr ? \'red\' : \'green\'">Text</div>'
})
export class StyleComp { isErr = true; }
Q12
Format a date using DatePipe.
Beginner
Problem: A raw JavaScript Date object needs to be displayed in a human-readable format.
Details: Use the | date pipe in the template. You can pass arguments like 'shortDate' to customize the output format without mutating the actual Date object.
@Component({
  imports: [DatePipe],
  template: '<p>{{ today | date:"shortDate" }}</p>'
})
export class DateComp { today = new Date(); }
Q13
Format a number as currency.
Beginner
Problem: Display a float value as standard US currency with a dollar sign.
Details: Use the | currency pipe. It automatically adds the correct symbol, commas, and restricts decimals based on the provided currency code.
@Component({
  imports: [CurrencyPipe],
  template: '<p>Total: {{ price | currency:"USD" }}</p>'
})
export class PriceComp { price = 199.99; }
Q14
Create a Custom Pipe.
Beginner
Problem: You need a reusable way to transform strings to fully uppercase across your app.
Details: Create a class decorated with @Pipe and implement the PipeTransform interface. The transform method takes the input and returns the transformed string.
@Pipe({ name: 'customUpper', standalone: true })
export class UpperPipe implements PipeTransform {
  transform(val: string): string {
    return val ? val.toUpperCase() : '';
  }
}
Q15
Create a Standalone Component.
Beginner
Problem: Create a modern component that doesn’t rely on being declared in an NgModule.
Details: Set standalone: true in the @Component decorator. This allows the component to directly import its own dependencies and be bootstrapped independently.
@Component({
  selector: 'app-solo',
  standalone: true,
  template: '<h1>Standalone!</h1>'
})
export class SoloComponent {}
Q16
Execute code on Initialization.
Beginner
Problem: Fetch data from an API exactly once when the component first appears.
Details: Implement the OnInit interface and place your logic inside ngOnInit(). This fires once after Angular has initialized data-bound input properties.
export class InitComp implements OnInit {
  ngOnInit() {
    console.log('Fired once on init!');
  }
}
Q17
Execute code on Destruction.
Beginner
Problem: Prevent memory leaks by clearing a setInterval when a component is removed from the DOM.
Details: Implement OnDestroy. The ngOnDestroy() method runs right before the component is destroyed, making it perfect for cleanup tasks.
export class DestroyComp implements OnDestroy {
  ngOnDestroy() {
    console.log('Clean up subscriptions here!');
  }
}
Q18
React to `@Input` changes.
Beginner
Problem: Execute specific logic every time a parent passes a new value to an @Input property.
Details: Implement OnChanges. The ngOnChanges() method provides a SimpleChanges object containing the previous and current values of all bound inputs.
export class ChangeComp implements OnChanges {
  @Input() data!: string;
  ngOnChanges(changes: SimpleChanges) {
    if (changes['data']) console.log('Changed!');
  }
}
Q19
Implement Content Projection (Slots).
Beginner
Problem: Create a reusable Card component that allows parents to pass arbitrary HTML inside it.
Details: Place the <ng-content></ng-content> tag inside the child component’s template. Anything the parent puts between the child’s opening and closing tags will be injected there.
// Child Template
`<div class="card"> <ng-content></ng-content> </div>`

// Parent Usage
<app-card> <h1>Projected!</h1> </app-card>
Q20
Use Template Reference Variables.
Beginner
Problem: Read the value of an input field directly in the HTML template without tying it to a component class variable.
Details: Assign a hash #varName to an element. You can then reference that DOM node and its properties anywhere else within the same template.
@Component({
  template: `
    <input #myInput />
    <button (click)="log(myInput.value)">Log</button>
  `
})
export class RefComp {
  log(v: string) { console.log(v); }
}
Q21
Create an Injectable Service.
Beginner
Problem: Create a reusable class to hold business logic and data that can be shared across multiple components.
Details: Use the @Injectable decorator. Setting providedIn: 'root' registers it as a singleton across the entire application automatically.
@Injectable({ providedIn: 'root' })
export class DataService {
  getItems() { return ['A', 'B']; }
}
Q22
Inject a Service using `inject()`.
Beginner
Problem: Access a service inside a component without using constructor injection.
Details: Angular v14 introduced the inject() function. It is cleaner than constructor injection and heavily utilized in modern function-based Angular patterns.
import { inject } from '@angular/core';

export class MyComp {
  private dataService = inject(DataService);
  items = this.dataService.getItems();
}
Q23
Define a basic route array.
Beginner
Problem: Map a URL path to load a specific component.
Details: Create an array of Route objects defining the path and the component that should render when the browser hits that URL.
export const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent }
];
Q24
Navigate using RouterLink.
Beginner
Problem: Create an anchor tag that navigates to a new route without causing a full browser page reload.
Details: Use the routerLink directive instead of href. It intercepts the click and updates the URL via Angular’s internal router.
// Requires importing RouterModule or RouterLink
@Component({
  imports: [RouterLink],
  template: '<a routerLink="/about">Go to About</a>'
})
Q25
Navigate programmatically.
Beginner
Problem: Redirect the user to a new route from inside a component method (e.g., after saving a form).
Details: Inject the Router service and call its navigate() method, passing an array representing the path segments.
export class SaveComp {
  private router = inject(Router);

  onSave() {
    // Save logic here...
    this.router.navigate(['/dashboard']);
  }
}

Intermediate Level (Forms, Routing & RxJS Basics)

Q26
Extract a static route parameter.
Intermediate
Problem: Read the id parameter from the URL /users/:id exactly once when the component loads.
Details: Inject ActivatedRoute and access the snapshot.paramMap. This is synchronous and ideal if the component never reuses the same instance for different URLs.
export class UserComp implements OnInit {
  private route = inject(ActivatedRoute);
  userId!: string;

  ngOnInit() {
    this.userId = this.route.snapshot.paramMap.get('id')!;
  }
}
Q27
Extract route parameters reactively.
Intermediate
Problem: Read URL parameters in a way that handles the URL changing while the component remains mounted.
Details: Subscribe to route.paramMap. Because it is an Observable, the callback fires every time the route parameter updates without unmounting the component.
export class UserComp implements OnInit {
  private route = inject(ActivatedRoute);

  ngOnInit() {
    this.route.paramMap.subscribe(params => {
      console.log('New ID:', params.get('id'));
    });
  }
}
Q28
Setup a Template-Driven form.
Intermediate
Problem: Create a form primarily governed by HTML attributes, capturing its value on submit.
Details: Import FormsModule. Use #form="ngForm" on the form tag and add the ngModel directive to inputs. Pass form.value to the submit handler.
@Component({
  template: `
    <form #f="ngForm" (ngSubmit)="submit(f.value)">
      <input name="email" ngModel required />
      <button [disabled]="f.invalid">Save</button>
    </form>
  `
})
export class FormComp {
  submit(val: any) { console.log(val); }
}
Q29
Setup a Reactive Form.
Intermediate
Problem: Define a complex form structure and validation rules directly in the TypeScript class.
Details: Inject FormBuilder to cleanly construct a FormGroup. Reactive forms offer better testability and synchronous access to form state.
export class ReactiveComp {
  private fb = inject(FormBuilder);
  
  myForm = this.fb.group({
    email: ['', [Validators.required, Validators.email]],
    age: [18, Validators.min(18)]
  });
}
Q30
Bind a Reactive Form to the template.
Intermediate
Problem: Connect the FormGroup instantiated in your class to the HTML form elements.
Details: Import ReactiveFormsModule. Apply [formGroup] to the `
` tag and use the formControlName string directive on inputs to link them to the class model.
<!-- Ensure ReactiveFormsModule is imported -->
<form [formGroup]="myForm">
  <input formControlName="email" />
  <input formControlName="age" type="number" />
</form>
Q31
Dynamically add controls with FormArray.
Intermediate
Problem: Create a form where users can add an unknown number of input fields (e.g., adding multiple aliases).
Details: Use FormBuilder.array(). You can programmatically push new FormControl or FormGroup objects into this array at runtime based on user action.
export class FormComp {
  private fb = inject(FormBuilder);
  aliases = this.fb.array([ this.fb.control('') ]);

  addAlias() {
    this.aliases.push(this.fb.control(''));
  }
}
Q32
Make a GET request using HttpClient.
Intermediate
Problem: Fetch data from a REST API endpoint and return it as an Observable.
Details: Inject HttpClient and call its get() method. Providing a generic type <Type> ensures the resulting Observable is strongly typed.
@Injectable({ providedIn: 'root' })
export class ApiService {
  private http = inject(HttpClient);

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }
}
Q33
Make a POST request using HttpClient.
Intermediate
Problem: Send JSON payload data to a server endpoint to create a new resource.
Details: Use http.post(). The first argument is the URL, and the second is the body payload. Angular automatically serializes the object to JSON.
export class ApiService {
  private http = inject(HttpClient);

  createUser(user: User) {
    return this.http.post('/api/users', user);
  }
}
Q34
Use the async pipe.
Intermediate
Problem: Render data from an Observable directly in the HTML without manually subscribing in the TypeScript class.
Details: The | async pipe subscribes to an Observable in the template, unwraps the emitted values, and automatically unsubscribes when the component is destroyed.
@Component({
  imports: [AsyncPipe],
  template: `
    @for (u of users$ | async; track u.id) {
      <div>{{ u.name }}</div>
    }
  `
})
export class UserComp {
  users$ = inject(ApiService).getUsers();
}
Q35
Catch errors in an RxJS stream.
Intermediate
Problem: Intercept a failed HTTP request to log the error and return a safe fallback value to the UI.
Details: Use the catchError operator within the pipe(). It must return a new Observable (like of([])) so the downstream subscriptions don’t crash.
this.http.get('/api/data').pipe(
  catchError(err => {
    console.error('Failed:', err);
    return of([]); // Fallback empty array
  })
);
Q36
Transform data with the map operator.
Intermediate
Problem: Filter or restructure the data coming from an HTTP response before it reaches the component.
Details: Use the map operator inside pipe(). It takes the emitted data, allows you to mutate/transform it, and passes the result down the observable chain.
this.http.get<User[]>('/api/users').pipe(
  // Map RxJS operator, calling native Array.filter
  map(users => users.filter(u => u.isActive))
);
Q37
Create state using BehaviorSubject.
Intermediate
Problem: Hold a piece of state in a service that requires an initial value and emits its current value to any new subscribers instantly.
Details: A BehaviorSubject holds the “current” value. Expose it as an Observable using asObservable() to prevent external classes from calling next().
export class StateService {
  private userSub = new BehaviorSubject<string>('Guest');
  user$ = this.userSub.asObservable();

  setUser(name: string) { this.userSub.next(name); }
}
Q38
Manually unsubscribe from an Observable.
Intermediate
Problem: Prevent memory leaks by destroying an active manual subscription when a component leaves the DOM.
Details: Store the returned Subscription object in a class property, and call unsubscribe() on it inside the ngOnDestroy lifecycle hook.
export class MyComp implements OnDestroy {
  private sub!: Subscription;

  ngOnInit() {
    this.sub = this.service.data$.subscribe();
  }
  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}
Q39
Cleanly unsubscribe using takeUntilDestroyed.
Intermediate
Problem: Unsubscribe from an Observable without writing boilerplate ngOnDestroy logic.
Details: Angular v16 introduced takeUntilDestroyed. Placed inside a pipe (and called in an injection context like the constructor), it automatically kills the stream on destroy.
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

export class ModernComp {
  constructor() {
    this.service.data$.pipe(
      takeUntilDestroyed()
    ).subscribe();
  }
}
Q40
Query a child component using @ViewChild.
Intermediate
Problem: A parent component needs to call a method or access properties directly on a child component.
Details: @ViewChild allows you to grab a reference to an element or component injected in the template. The reference is guaranteed to be available by ngAfterViewInit.
export class ParentComp implements AfterViewInit {
  @ViewChild(ChildComp) child!: ChildComp;

  ngAfterViewInit() {
    this.child.childMethod();
  }
}
Q41
Query projected content using @ContentChild.
Intermediate
Problem: A wrapper component needs to inspect or access a component passed into it via <ng-content>.
Details: While ViewChild looks at the component’s own template, @ContentChild looks at the nodes projected into it from the parent. Available by ngAfterContentInit.
export class WrapperComp implements AfterContentInit {
  @ContentChild(HeaderComp) header!: HeaderComp;

  ngAfterContentInit() {
    console.log(this.header);
  }
}
Q42
Lazy load a standalone route.
Intermediate
Problem: Defer downloading a component’s JavaScript bundle until the user actually navigates to that route.
Details: Use the loadComponent property in your route definition paired with a dynamic import() statement pointing to the standalone component file.
export const routes: Routes = [
  { 
    path: 'admin', 
    loadComponent: () => import('./admin.comp').then(c => c.AdminComp)
  }
];
Q43
Bind to the Host element’s properties.
Intermediate
Problem: A directive or component needs to apply a CSS class or attribute directly to its own hosting DOM element.
Details: Use the @HostBinding decorator. It binds a host element property (like a class or style) to a variable inside the directive class.
@Directive({ selector: '[appHighlight]', standalone: true })
export class HighlightDirective {
  @HostBinding('class.highlighted') isHigh = true;
}
Q44
Listen to Host element events.
Intermediate
Problem: A directive needs to trigger a function when the user clicks or hovers over the element it is attached to.
Details: Use the @HostListener decorator. It automatically listens for standard DOM events on the host element and triggers the bound class method.
@Directive({ selector: '[appClickTrack]', standalone: true })
export class ClickTrackDirective {
  @HostListener('click', ['$event'])
  onClick(e: Event) { console.log('Clicked!', e); }
}
Q45
Combine multiple Observables.
Intermediate
Problem: You need to merge data from a User stream and a Posts stream before rendering the UI.
Details: Use the RxJS combineLatest function. It waits for all provided observables to emit at least once, then emits an array of their latest values whenever any of them update.
vm$ = combineLatest([this.users$, this.posts$]).pipe(
  map(([users, posts]) => ({ users, posts }))
);
Q46
Avoid duplicate HTTP calls using shareReplay.
Intermediate
Problem: An HTTP observable is subscribed to by multiple components, causing the network request to fire redundantly for each subscriber.
Details: Add shareReplay(1) to the end of the pipe. It multicasts the observable and caches the last emitted value, serving it instantly to late subscribers.
export class DataService {
  config$ = this.http.get('/api/config').pipe(
    shareReplay(1)
  );
}
Q47
Setup a wildcard (404) route.
Intermediate
Problem: Redirect the user to a “Not Found” component if they type an invalid URL.
Details: Add a route at the very bottom of your routing array with the path '**'. The router checks top-down, so this acts as a catch-all.
export const routes: Routes = [
  { path: '', component: HomeComp },
  { path: '**', component: NotFoundComp } 
];
Q48
Setup a functional Route Guard.
Intermediate
Problem: Prevent unauthorized users from accessing a specific route in your application.
Details: Modern Angular replaces Guard Classes with CanActivateFn functions. Use inject() to access auth services and return a boolean or a redirect UrlTree.
export const authGuard: CanActivateFn = () => {
  const isAuth = inject(AuthService).isLoggedIn;
  return isAuth ? true : inject(Router).parseUrl('/login');
};

// Route config: { path: 'dash', canActivate: [authGuard] }
Q49
Attach HTTP Headers to a request.
Intermediate
Problem: Send custom metadata, like a specific Content-Type or Auth token, with an individual HttpClient request.
Details: Pass an options object as the final parameter to the http.get/post method containing an instance of HttpHeaders.
const headers = new HttpHeaders({ 'X-Custom-Header': 'Value' });

this.http.get('/api/data', { headers }).subscribe();
Q50
Create a Custom Form Validator.
Intermediate
Problem: Enforce a rule that a specific input cannot contain the word "admin".
Details: A custom validator is simply a function that takes an AbstractControl. It returns null if valid, or a ValidationErrors object if invalid.
export function noAdminValidator(): ValidatorFn {
  return (ctrl: AbstractControl): ValidationErrors | null => {
    const isForbidden = ctrl.value?.includes('admin');
    return isForbidden ? { noAdmin: true } : null;
  };
}

Advanced Level (Architecture, Signals, & RxJS Patterns)

Q51
Create an HTTP Interceptor.
Advanced
Problem: Automatically attach a JWT Bearer token to every outgoing HTTP request globally.
Details: Create an HttpInterceptorFn. It intercepts the HttpRequest, allows you to clone and mutate headers, and forwards it via the next() handler.
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const token = 'my-jwt-token';
  const cloned = req.clone({ 
    setHeaders: { Authorization: `Bearer ${token}` } 
  });
  return next(cloned);
};
Q52
Cancel previous API requests using switchMap.
Advanced
Problem: Prevent race conditions in an autocomplete search where typing fast triggers multiple overlapping requests.
Details: switchMap maps values to an inner observable. Crucially, if a new value arrives before the inner observable finishes, it cancels/aborts the previous network request.
this.searchControl.valueChanges.pipe(
  switchMap(term => this.http.get(`/api/search?q=${term}`))
).subscribe(results => console.log(results));
Q53
Queue operations safely using concatMap.
Advanced
Problem: Process multiple HTTP POST requests in strict order, ensuring one finishes before the next begins.
Details: Unlike switchMap (cancels) or mergeMap (runs in parallel), concatMap waits for the previous inner observable to complete before executing the next one.
this.saveSubject.pipe(
  concatMap(data => this.http.post('/api/save', data))
).subscribe();
Q54
Debounce an input stream.
Advanced
Problem: Wait until a user has paused typing for 300ms before sending a value down the observable chain.
Details: Use debounceTime(ms). It discards emitted values that take less than the specified time between outputs. Pair with distinctUntilChanged() to ignore identical consecutive values.
this.inputControl.valueChanges.pipe(
  debounceTime(300),
  distinctUntilChanged()
).subscribe(val => console.log(val));
Q55
Implement an Async Form Validator.
Advanced
Problem: Validate an input field by querying a database to see if a username is already taken.
Details: An AsyncValidatorFn returns an Observable instead of a static value. Angular waits for the observable to complete before updating the form's validity state.
export function emailTakenValidator(api: ApiService): AsyncValidatorFn {
  return (ctrl) => api.checkEmail(ctrl.value).pipe(
    map(isTaken => isTaken ? { emailTaken: true } : null)
  );
}
Q56
Boost performance with OnPush Change Detection.
Advanced
Problem: A heavy component re-renders unnecessarily whenever a global application state changes.
Details: Setting ChangeDetectionStrategy.OnPush tells Angular to skip checking this component unless its @Input() object references change, or an event originates from it.
@Component({
  selector: 'app-fast',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: '<p>{{ data.val }}</p>'
})
export class FastComp { @Input() data: any; }
Q57
Manually trigger Change Detection.
Advanced
Problem: An OnPush component updates a variable via a setTimeout or external observable, but the UI doesn't refresh.
Details: Inject ChangeDetectorRef and call markForCheck(). This flags the component's branch so Angular knows to evaluate it during the next tick.
export class ManualComp {
  private cdr = inject(ChangeDetectorRef);

  updateLocalData() {
    this.data.val = 'New';
    this.cdr.markForCheck(); 
  }
}
Q58
Bind route parameters directly to component Inputs.
Advanced
Problem: Avoid writing boilerplate ActivatedRoute subscription logic just to read a simple URL parameter.
Details: Use withComponentInputBinding() in your router config. Angular will automatically push URL params (like /user/:id) directly into matching @Input() properties.
// In app.config.ts router setup:
provideRouter(routes, withComponentInputBinding())

// In UserComponent (URL: /user/42):
@Input() id!: string; // Automatically gets '42'
Q59
Handle navigation cancellation gracefully.
Advanced
Problem: Warn a user if they attempt to navigate away from a form with unsaved changes.
Details: Create a CanDeactivateFn guard. If the function returns false (or a confirmed prompt returning false), the Angular router aborts the navigation attempt entirely.
export const unsavedGuard: CanDeactivateFn<any> = (comp) => {
  if (comp.hasUnsavedChanges) {
    return confirm('Unsaved changes. Leave?');
  }
  return true;
};
Q60
Create and update a basic Signal.
Advanced
Problem: Use Angular's modern reactivity model to hold primitive state without relying on RxJS or Zone.js hooks.
Details: Use the signal() function. Read it by calling it like a function count(), and mutate it using .set(val) or .update(fn).
import { signal } from '@angular/core';

export class CounterComp {
  count = signal(0);

  increment() {
    this.count.update(c => c + 1);
  }
}
Q61
Compute derived state using Signals.
Advanced
Problem: Create a reactive variable that automatically recalculates whenever its dependent signals change.
Details: Use the computed() function. It caches its calculation and only re-evaluates lazily when the internal signals it reads notify it of a change.
export class CartComp {
  price = signal(100);
  tax = computed(() => this.price() * 0.2);
  total = computed(() => this.price() + this.tax());
}
Q62
Run side effects reacting to a Signal.
Advanced
Problem: Log a message or execute imperative code whenever a specific signal's value changes.
Details: Use effect(). It registers a reactive side-effect that automatically tracks any signals read within its closure, re-running asynchronously when they update.
export class LogComp {
  id = signal(1);
  
  constructor() {
    effect(() => {
      console.log('ID is now:', this.id());
    });
  }
}
Q63
Define a Signal Input.
Advanced
Problem: Replace the traditional @Input() decorator with the modern, safer Signal-based input API.
Details: Use the input() or input.required() function. It exposes the passed data as a read-only Signal, ensuring perfect type safety and reactive integration.
import { input } from '@angular/core';

export class UserCardComp {
  userId = input.required<number>();
  theme = input('light'); // Optional with default
}
Q64
Use @defer for lazy loading components in the template.
Advanced
Problem: You want to split a heavy charting library into a separate JS chunk that only loads when scrolled into view.
Details: Use the @defer block with a trigger like on viewport. It handles chunking automatically without router configurations, providing a @placeholder while waiting.
@Component({
  template: `
    @defer (on viewport) {
      <heavy-chart />
    } @placeholder {
      <div>Scroll down to load chart...</div>
    }
  `
})
export class DashComp {}
Q65
Implement ControlValueAccessor.
Advanced
Problem: Build a custom UI component (like a star rating) that plugs seamlessly into Angular's formControlName or ngModel directives.
Details: Implement ControlValueAccessor and provide it via NG_VALUE_ACCESSOR. This interface acts as the bridge translating Angular form APIs to your component's internal state.
@Component({
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: CustomInput, multi: true }]
})
export class CustomInput implements ControlValueAccessor {
  value = '';
  onChange = (val: any) => {};
  
  writeValue(val: any) { this.value = val; }
  registerOnChange(fn: any) { this.onChange = fn; }
  registerOnTouched(fn: any) {}
}
Q66
Provide environment variables via InjectionToken.
Advanced
Problem: Inject a static configuration string (like an API URL) into a service safely without hardcoding it.
Details: Instantiate an InjectionToken. Provide a value for it in your app's config array, and consume it using the standard inject() function.
export const API_URL = new InjectionToken<string>('API_URL');

// In app.config.ts
{ provide: API_URL, useValue: 'https://api.com' }

// In Service
private url = inject(API_URL);
Q67
Create an Observable manually from a DOM event.
Advanced
Problem: Turn a raw browser event (like document clicks) into a subscribable RxJS stream without using fromEvent.
Details: Instantiate a new Observable. Bind the native listener inside, emit via subscriber.next(), and return a teardown function that removes the listener.
const clicks$ = new Observable(sub => {
  const handler = (e: Event) => sub.next(e);
  document.addEventListener('click', handler);
  return () => document.removeEventListener('click', handler);
});
Q68
Handle errors globally.
Advanced
Problem: Catch all unhandled UI exceptions across the entire app to log them to a telemetry service like Sentry.
Details: Create a class implementing ErrorHandler and override its handleError method. Provide this class in your application root providers.
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: any) {
    console.error('Global Catch:', error);
    // Post to Sentry/DataDog here
  }
}
// Provider: { provide: ErrorHandler, useClass: GlobalErrorHandler }
Q69
Dynamically create a component.
Advanced
Problem: Render a component into the DOM purely via TypeScript logic, without declaring it in an HTML template.
Details: Inject ViewContainerRef. Call createComponent() passing the Component class. You can assign inputs directly to the returned reference's instance.
export class HostComp {
  private vcr = inject(ViewContainerRef);

  loadComp() {
    this.vcr.clear();
    const ref = this.vcr.createComponent(DynamicComp);
    ref.instance.data = 'Passed programmatically';
  }
}
Q70
Convert an Observable to a Signal.
Advanced
Problem: Bridge an existing RxJS data stream (like an HTTP fetch) into Angular's modern Signal ecosystem.
Details: Use toSignal() from @angular/core/rxjs-interop. It subscribes to the observable instantly and returns a read-only Signal representing the latest value.
import { toSignal } from '@angular/core/rxjs-interop';

export class DataComp {
  private data$ = this.http.get('/api/data');
  dataSig = toSignal(this.data$); 
}
Q71
Convert a Signal to an Observable.
Advanced
Problem: You have a Signal but need to pass its value into a legacy RxJS pipe chain (like switchMap).
Details: Use toObservable(). It tracks the signal using an effect() internally and pushes new values to subscribers whenever the signal updates.
import { toObservable } from '@angular/core/rxjs-interop';

export class RxjsBridgeComp {
  count = signal(0);
  count$ = toObservable(this.count);

  constructor() { 
    this.count$.subscribe(c => console.log(c)); 
  }
}
Q72
Cache HTTP requests via Interceptors.
Advanced
Problem: Prevent duplicate network calls for identical URLs by intercepting requests and returning cached responses.
Details: Maintain a Map dictionary. Check if the URL exists in the map; if yes, return of(cachedResponse). If no, pass the request on and use tap to save the final response into the map.
const cache = new Map<string, HttpResponse<any>>();

export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
  if (req.method !== 'GET') return next(req);
  if (cache.has(req.urlWithParams)) return of(cache.get(req.urlWithParams)!);
  
  return next(req).pipe(
    tap(res => { if (res instanceof HttpResponse) cache.set(req.urlWithParams, res); })
  );
};
Q73
Implement a highly-optimized trackBy function.
Advanced
Problem: Write a generic tracking function to prevent DOM recreation in legacy *ngFor loops.
Details: Create a factory function returning a TrackByFunction. This keeps code DRY if you map many arrays by generic keys like 'id'. (Note: mostly obsolete with `@for`).
export function trackByProp<T>(prop: keyof T): TrackByFunction<T> {
  return (_, item) => item[prop];
}

// Component: trackById = trackByProp('id');
// Template: *ngFor="let i of items; trackBy: trackById"
Q74
Execute code strictly on App Startup.
Advanced
Problem: Halt the application from rendering until a critical configuration file is fetched via API.
Details: Use APP_INITIALIZER. Provide a factory function that returns a Promise or Observable. Angular blocks bootstrapping until it resolves.
export function initApp(config: ConfigService) {
  return () => config.load(); // Returns Promise
}

// Providers:
{ provide: APP_INITIALIZER, useFactory: initApp, deps: [ConfigService], multi: true }
Q75
Create a two-way bound Model Signal.
Advanced
Problem: Implement standard two-way data binding (like `[(ngModel)]`) using the modern Signal API.
Details: Use the model() function. It acts as both an Input (receives data from parent) and an Output (emits updates back to parent implicitly).
import { model } from '@angular/core';

export class CheckboxComp {
  // Parent uses: [(checked)]="val"
  checked = model(false);

  toggle() {
    this.checked.update(v => !v);
  }
}

Expert Level (Performance, SSR, Architecture)

Q76
Prevent zone.js from tracking DOM events.
Expert
Problem: A rapid firing event (like scroll or mousemove) triggers Change Detection thousands of times, freezing the UI.
Details: Inject NgZone and wrap the event listener inside runOutsideAngular. The callback will execute invisibly to the change detection engine.
export class ScrollComp implements OnInit {
  private ngZone = inject(NgZone);

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      window.addEventListener('scroll', () => {
        // Heavy logic here
      });
    });
  }
}
Q77
Re-enter Angular Zone from an external callback.
Expert
Problem: After finishing heavy work outside the Angular zone, you need to update a component state and reflect it in the DOM.
Details: Inside your outside-zone code, wrap the state update logic in ngZone.run(). This forcefully brings execution back into Angular's purview, triggering a render tick.
this.ngZone.runOutsideAngular(() => {
  heavyApi((result) => {
    this.ngZone.run(() => {
      this.data = result; // UI updates now
    });
  });
});
Q78
Isolate code from executing during SSR.
Expert
Problem: Your component uses window.localStorage, which crashes the Node.js server during Server-Side Rendering.
Details: Inject PLATFORM_ID and evaluate it using isPlatformBrowser(). Wrap the problematic DOM-specific APIs in this conditional.
import { isPlatformBrowser } from '@angular/common';

export class SsrComp {
  private platformId = inject(PLATFORM_ID);

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      window.localStorage.setItem('key', 'val');
    }
  }
}
Q79
Transfer state from Server to Client (SSR).
Expert
Problem: Prevent the browser from re-fetching the exact same API data that the Node server already fetched during the SSR process.
Details: Inject TransferState. The server caches the API response in an inline script block. The client intercepts the fetch, checks the TransferState key, and uses the cached data instead.
export class DataSvc {
  private transferState = inject(TransferState);
  private KEY = makeStateKey<any>('MY_DATA');

  getData() {
    if (this.transferState.hasKey(this.KEY)) {
      return of(this.transferState.get(this.KEY, null));
    }
    return this.http.get('/api').pipe(
      tap(data => this.transferState.set(this.KEY, data))
    );
  }
}
Q80
Build a Custom Method Decorator.
Expert
Problem: You want a clean, reusable way to log the exact execution time of a method simply by adding @LogTime() above it.
Details: Create a factory function returning a PropertyDescriptor manipulator. It overrides the original method with a wrapper that runs console.time around the execution block.
export function LogTime() {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      console.time(key);
      const result = original.apply(this, args);
      console.timeEnd(key);
      return result;
    };
  };
}
Q81
Preload all lazy modules automatically.
Expert
Problem: Maximize performance by downloading lazy-loaded JS bundles in the background immediately after the initial page renders.
Details: Wrap PreloadAllModules inside the withPreloading() function in the router configuration.
// In app.config.ts
provideRouter(routes, withPreloading(PreloadAllModules))
Q82
Write a Custom Preloading Strategy.
Expert
Problem: Only preload specific lazy modules (e.g., highly trafficked features) while keeping heavy, rarely-used modules purely lazy.
Details: Implement the PreloadingStrategy interface. Check custom route.data tags (like preload: true) to decide whether to invoke the load() callback.
@Injectable({ providedIn: 'root' })
export class FlaggedPreloadStrategy implements PreloadingStrategy {
  preload(route: Route, load: () => Observable<any>): Observable<any> {
    return route.data?.['preload'] ? load() : of(null);
  }
}
Q83
Compile Angular Components to Web Components.
Expert
Problem: Render an Angular component natively inside a React, Vue, or vanilla HTML application using browser APIs.
Details: Use @angular/elements. Use createCustomElement to bridge the Angular lifecycle to the native Custom Element specification.
// In main.ts
import { createCustomElement } from '@angular/elements';

createApplication(appConfig).then(appRef => {
  const el = createCustomElement(WidgetComp, { injector: appRef.injector });
  customElements.define('my-widget', el);
});
Q84
Inject dependencies outside of constructors.
Expert
Problem: You need to use inject() inside a vanilla JavaScript function that is executed outside of Angular's standard initialization cycle.
Details: Use runInInjectionContext. Pass it a reference to an active Injector to manually create an injection context environment.
import { Injector, runInInjectionContext } from '@angular/core';

function executeLogic(injector: Injector) {
  runInInjectionContext(injector, () => {
    const service = inject(MyService);
    service.process();
  });
}
Q85
Set up a basic Web Worker.
Expert
Problem: Offload a massive CPU-intensive calculation (like processing millions of rows) away from the main UI thread.
Details: Generate a worker file using Angular CLI. Instantiate the worker pointing to that URL, post messages to it, and listen for the asynchronous result without blocking rendering.
// ng generate web-worker my-worker
if (typeof Worker !== 'undefined') {
  const worker = new Worker(new URL('./app.worker', import.meta.url));
  
  worker.onmessage = ({ data }) => console.log('Result:', data);
  worker.postMessage('compute');
}
Q86
Dynamically swap CSS stylesheets at runtime.
Expert
Problem: Implement a full dark/light theme toggle that replaces the underlying application CSS file dynamically.
Details: Inject the DOCUMENT token. Locate the <link rel="stylesheet"> tag in the head and mutate its href property directly.
export class ThemeService {
  private doc = inject(DOCUMENT);

  setTheme(name: string) {
    let link = this.doc.getElementById('theme-css') as HTMLLinkElement;
    if (!link) {
      link = this.doc.createElement('link');
      link.id = 'theme-css'; link.rel = 'stylesheet';
      this.doc.head.appendChild(link);
    }
    link.href = `${name}.css`;
  }
}
Q87
Provide multiple values under one Injection Token.
Expert
Problem: Create an extensible plugin architecture where multiple distinct services register themselves under the same global token.
Details: Use multi: true in the provider configuration. When a consumer injects the token, Angular resolves it into an array of all provided instances.
// Providers:
{ provide: PLUGINS, useValue: PluginA, multi: true },
{ provide: PLUGINS, useValue: PluginB, multi: true }

// Consumer receives an array:
private plugins = inject(PLUGINS); // [PluginA, PluginB]
Q88
Handle Circular Dependencies.
Expert
Problem: Two classes reference each other, or a provider config needs to reference a class before it is technically defined in the file.
Details: Wrap the class reference in forwardRef(). This tells Angular's dependency injection system to wait and evaluate the reference lazily.
import { forwardRef } from '@angular/core';

@Component({
  providers: [{ 
    provide: ParentToken, 
    useExisting: forwardRef(() => ChildClass) 
  }]
})
export class ChildClass {}
Q89
Bypass the DomSanitizer safely.
Expert
Problem: Render raw HTML strings containing inline styles or scripts (like a trusted CMS output) which Angular strips by default.
Details: Inject DomSanitizer and call bypassSecurityTrustHtml(). This disables XSS protection for that string—use only with fully trusted backend data.
export class HtmlComp {
  private sanitizer = inject(DomSanitizer);
  safeHtml: SafeHtml;

  setHtml(dirtyHtml: string) {
    this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(dirtyHtml);
  }
}
Q90
Catch Unhandled RxJS errors globally.
Expert
Problem: Streams that error out without a local catchError block crash the subscription silently; you need to log them.
Details: Import the global config object from rxjs. Assign a callback to onUnhandledError directly in your main.ts initialization file.
import { config } from 'rxjs';

// Place in main.ts
config.onUnhandledError = (err) => {
  console.error('Missed by catchError:', err);
};
Q91
Map routes conditionally based on screen size.
Expert
Problem: Serve a completely different component for the same /dashboard URL based on whether the user is on mobile or desktop.
Details: Create a custom matcher function in the routing config. It examines custom logic (like `window.innerWidth`) and returns consumed segments if it matches.
export function mobileMatch(url: UrlSegment[]) {
  if (window.innerWidth < 768 && url[0].path === 'dash') {
    return { consumed: url };
  }
  return null;
}
// Route config: { matcher: mobileMatch, component: MobileDashComp }
Q92
Extract raw DOM elements from an `ng-template`.
Expert
Problem: You have an <ng-template> but need to programmatically access its raw HTML nodes to pass to a non-Angular charting library.
Details: Use ViewContainerRef.createEmbeddedView. It instantiates the template in memory, allowing you to access the raw DOM objects via view.rootNodes.
@ViewChild('tpl') tpl!: TemplateRef<any>;
private vcr = inject(ViewContainerRef);

extract() {
  const view = this.tpl.createEmbeddedView(null);
  // view.rootNodes[0] is the raw DOM element
}
Q93
Prevent SSR chunks from hydrating.
Expert
Problem: Render a heavy static footer on the server, but completely prevent Angular from wasting CPU attaching event listeners to it on the client.
Details: Use the @defer (hydrate never) block. It instructs the client hydration process to completely ignore the HTML block shipped by the server.
<!-- Leaves it strictly as static HTML -->
@defer (hydrate never) {
  <heavy-static-footer />
}
Q94
Inherit components cleanly without `super()`.
Expert
Problem: Create an abstract base class that injects 5 services, without forcing child classes to manually inject and pass them via super(s1, s2...).
Details: Use the inject() function inside the abstract class properties. This completely decouples dependency injection from the class constructor hierarchy.
export abstract class BaseComp {
  protected api = inject(ApiService); 
}

@Component({...})
export class ChildComp extends BaseComp {
  doWork() { this.api.get(); } // Clean inheritance
}
Q95
Override an external library component's behavior.
Expert
Problem: You use a third-party UI library button, but need to automatically attach custom logic to every instance without modifying the library source.
Details: Create a standalone directive whose selector exactly matches the third-party component's tag. Angular applies your directive alongside their component.
@Directive({
  selector: 'mat-button', // Hijacks Material Button
  standalone: true
})
export class ButtonOverrideDirective {
  constructor(private el: ElementRef) {
    this.el.nativeElement.style.borderRadius = '8px';
  }
}
Q96
Create an Angular Micro-frontend.
Expert
Problem: Expose a single Angular component so an entirely different Host application can load it over the network at runtime.
Details: Utilize Webpack 5 Module Federation in your build configuration to expose the compiled file mapped to a remote entry point.
// In webpack.config.js
plugins: [
  new ModuleFederationPlugin({
    name: 'remoteApp',
    filename: 'remoteEntry.js',
    exposes: {
      './Widget': './src/app/widget.component.ts',
    },
    shared: { '@angular/core': { singleton: true } }
  })
]
Q97
Manage Complex State using NgRx Signal Store.
Expert
Problem: Create a highly performant, boilerplate-free state machine relying entirely on Signals instead of RxJS observables.
Details: Use @ngrx/signals. It provides a functional signalStore API integrating state slices, computed values, and update methods seamlessly.
import { signalStore, withState, withMethods } from '@ngrx/signals';

export const UserStore = signalStore(
  { providedIn: 'root' },
  withState({ users: [] }),
  withMethods(store => ({
    add(user) { patchState(store, { users: [...store.users(), user] }); }
  }))
);
Q98
Apply multiple directives to a component dynamically via metadata.
Expert
Problem: Apply reusable behavior (like tooltip and ripple effect directives) automatically when a specific component is used.
Details: Use the hostDirectives property in the component decorator. It composes standalone directives directly onto the host element at compilation.
@Component({
  selector: 'app-special-button',
  hostDirectives: [TooltipDirective, RippleDirective],
  template: '<button>Hover Me</button>'
})
export class SpecialBtnComp {}
Q99
Safely mutate DOM elements without direct native Element access.
Expert
Problem: You must append a class to a DOM node, but nativeElement.classList.add causes issues in Service Workers or SSR.
Details: Inject Renderer2. It abstracts DOM manipulations so they execute safely across all host environments (Browser, Server, Web Worker).
export class SafeComp {
  private renderer = inject(Renderer2);
  private el = inject(ElementRef);

  modify() {
    this.renderer.addClass(this.el.nativeElement, 'safe-class');
  }
}
Q100
Fix ExpressionChangedAfterItHasBeenCheckedError gracefully.
Expert
Problem: A child component updates a parent's bound variable immediately during the initialization cycle, causing Angular's unidirectional data flow check to crash.
Details: You must defer the update until the next JavaScript macro/micro task so the current Change Detection tick finishes first. Wrap it in a resolved Promise.
export class ChildComp implements OnInit {
  @Output() ready = new EventEmitter<boolean>();

  ngOnInit() {
    // Defers emit until current synchronous CD finishes
    Promise.resolve().then(() => this.ready.emit(true));
  }
}