Advanced Angular Interview Questions with Code Examples

Advanced Angular Interview Questions with Code Examples

Component Communication & State Management

1. Parent-Child Communication with Input/Output and Service

Parent Component

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

@Component({
  selector: 'app-parent',
  template: `
    <div class="parent">
      <h2>Parent Component</h2>
      <div>
        <input [(ngModel)]="parentMessage" placeholder="Message for child">
        <button (click)="sendMessage()">Send via Service</button>
        <button (click)="updateDirectMessage()">Send via @Input</button>
      </div>
      
      <app-child 
        [directMessage]="directMessage"
        (messageEvent)="receiveMessage($event)">
      </app-child>
      
      <div *ngIf="childMessage" class="message-box">
        <p><strong>Message from Child:</strong> {{ childMessage }}</p>
      </div>
    </div>
  `,
  styles: [`
    .parent { padding: 20px; border: 2px solid blue; margin: 10px; }
    .message-box { margin-top: 20px; padding: 10px; background-color: #f0f0f0; }
  `]
})
export class ParentComponent {
  parentMessage = '';
  directMessage = '';
  childMessage = '';
  
  constructor(private dataService: DataService) {
    // Subscribe to messages from the child via service
    this.dataService.childMessage$.subscribe(message => {
      this.childMessage = message;
    });
  }
  
  sendMessage() {
    this.dataService.sendToChild(this.parentMessage);
  }
  
  updateDirectMessage() {
    this.directMessage = this.parentMessage;
  }
  
  receiveMessage(message: string) {
    this.childMessage = message;
  }
}

Child Component

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

@Component({
  selector: 'app-child',
  template: `
    <div class="child">
      <h3>Child Component</h3>
      
      <div *ngIf="directMessage" class="message-box">
        <p><strong>Direct Message:</strong> {{ directMessage }}</p>
      </div>
      
      <div *ngIf="serviceMessage" class="message-box">
        <p><strong>Service Message:</strong> {{ serviceMessage }}</p>
      </div>
      
      <div>
        <input [(ngModel)]="childReply" placeholder="Reply to parent">
        <button (click)="sendViaOutput()">Reply via @Output</button>
        <button (click)="sendViaService()">Reply via Service</button>
      </div>
    </div>
  `,
  styles: [`
    .child { padding: 15px; border: 2px solid green; margin: 10px; }
    .message-box { margin: 10px 0; padding: 10px; background-color: #e0f0e0; }
  `]
})
export class ChildComponent implements OnInit {
  @Input() directMessage = '';
  @Output() messageEvent = new EventEmitter<string>();
  
  serviceMessage = '';
  childReply = '';
  
  constructor(private dataService: DataService) {}
  
  ngOnInit() {
    // Subscribe to messages from the parent via service
    this.dataService.parentMessage$.subscribe(message => {
      this.serviceMessage = message;
    });
  }
  
  sendViaOutput() {
    this.messageEvent.emit(this.childReply);
  }
  
  sendViaService() {
    this.dataService.sendToParent(this.childReply);
  }
}

Shared Service

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

@Injectable({
  providedIn: 'root'
})
export class DataService {
  // Subjects for bi-directional communication
  private parentMessageSource = new Subject<string>();
  private childMessageSource = new Subject<string>();
  
  // Observable streams
  parentMessage$ = this.parentMessageSource.asObservable();
  childMessage$ = this.childMessageSource.asObservable();
  
  // Method for parent to send message to child
  sendToChild(message: string) {
    this.parentMessageSource.next(message);
  }
  
  // Method for child to send message to parent
  sendToParent(message: string) {
    this.childMessageSource.next(message);
  }
}

2. Custom State Management with Observables

// state.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

// Interface for our application state
export interface AppState {
  user: {
    id: number;
    name: string;
    isAuthenticated: boolean;
  };
  todos: Todo[];
  uiState: {
    isLoading: boolean;
    darkMode: boolean;
    sidebarOpen: boolean;
  };
}

export interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

// Initial state
const initialState: AppState = {
  user: {
    id: 0,
    name: '',
    isAuthenticated: false
  },
  todos: [],
  uiState: {
    isLoading: false,
    darkMode: false,
    sidebarOpen: true
  }
};

@Injectable({
  providedIn: 'root'
})
export class StateService {
  // BehaviorSubject containing the state
  private state$ = new BehaviorSubject<AppState>(initialState);
  
  // Method to get the current state
  getState(): Observable<AppState> {
    return this.state$.asObservable();
  }
  
  // Method to get a specific slice of state
  select<T>(selector: (state: AppState) => T): Observable<T> {
    return this.state$.pipe(
      map(state => selector(state))
    );
  }
  
  // Update state method (immutable pattern)
  setState(stateFn: (state: AppState) => AppState): void {
    const currentState = this.state$.getValue();
    const newState = stateFn(currentState);
    this.state$.next(newState);
  }
  
  // User actions
  login(id: number, name: string): void {
    this.setState(state => ({
      ...state,
      user: {
        id,
        name,
        isAuthenticated: true
      }
    }));
  }
  
  logout(): void {
    this.setState(state => ({
      ...state,
      user: {
        id: 0,
        name: '',
        isAuthenticated: false
      }
    }));
  }
  
  // Todo actions
  addTodo(text: string): void {
    const newTodo: Todo = {
      id: Date.now(),
      text,
      completed: false
    };
    
    this.setState(state => ({
      ...state,
      todos: [...state.todos, newTodo]
    }));
  }
  
  toggleTodo(id: number): void {
    this.setState(state => ({
      ...state,
      todos: state.todos.map(todo => 
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    }));
  }
  
  removeTodo(id: number): void {
    this.setState(state => ({
      ...state,
      todos: state.todos.filter(todo => todo.id !== id)
    }));
  }
  
  // UI state actions
  setLoading(isLoading: boolean): void {
    this.setState(state => ({
      ...state,
      uiState: {
        ...state.uiState,
        isLoading
      }
    }));
  }
  
  toggleDarkMode(): void {
    this.setState(state => ({
      ...state,
      uiState: {
        ...state.uiState,
        darkMode: !state.uiState.darkMode
      }
    }));
  }
  
  toggleSidebar(): void {
    this.setState(state => ({
      ...state,
      uiState: {
        ...state.uiState,
        sidebarOpen: !state.uiState.sidebarOpen
      }
    }));
  }
}

Usage in Component

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { StateService, Todo } from './state.service';

@Component({
  selector: 'app-root',
  template: `
    <div [class.dark-mode]="darkMode$ | async">
      <h1>Todo App</h1>
      
      <div *ngIf="(isAuthenticated$ | async); else loginBlock">
        <h2>Welcome, {{ userName$ | async }}!</h2>
        <button (click)="logout()">Logout</button>
        
        <div class="todo-form">
          <input [(ngModel)]="newTodo" placeholder="Add a new task...">
          <button (click)="addTodo()">Add</button>
        </div>
        
        <div *ngIf="(isLoading$ | async)" class="loader">Loading...</div>
        
        <ul class="todo-list">
          <li *ngFor="let todo of todos$ | async" [class.completed]="todo.completed">
            <span (click)="toggleTodo(todo.id)">{{ todo.text }}</span>
            <button (click)="removeTodo(todo.id)">✕</button>
          </li>
        </ul>
      </div>
      
      <ng-template #loginBlock>
        <div class="login-form">
          <h2>Please Login</h2>
          <button (click)="login()">Login as Demo User</button>
        </div>
      </ng-template>
      
      <div class="settings">
        <button (click)="toggleDarkMode()">Toggle Dark Mode</button>
        <button (click)="toggleSidebar()">Toggle Sidebar</button>
      </div>
    </div>
  `,
  styles: [`
    .dark-mode { background-color: #333; color: white; }
    .completed { text-decoration: line-through; color: #888; }
    .loader { margin: 10px 0; color: orange; }
  `]
})
export class AppComponent implements OnInit {
  newTodo = '';
  
  // Selectors for specific state slices
  todos$: Observable<Todo[]>;
  isAuthenticated$: Observable<boolean>;
  userName$: Observable<string>;
  isLoading$: Observable<boolean>;
  darkMode$: Observable<boolean>;
  
  constructor(private stateService: StateService) {
    // Select specific slices of state
    this.todos$ = this.stateService.select(state => state.todos);
    this.isAuthenticated$ = this.stateService.select(state => state.user.isAuthenticated);
    this.userName$ = this.stateService.select(state => state.user.name);
    this.isLoading$ = this.stateService.select(state => state.uiState.isLoading);
    this.darkMode$ = this.stateService.select(state => state.uiState.darkMode);
  }
  
  ngOnInit() {
    // Example of showing loading state
    this.stateService.setLoading(true);
    setTimeout(() => {
      this.stateService.setLoading(false);
    }, 1000);
  }
  
  login() {
    this.stateService.login(1, 'Demo User');
  }
  
  logout() {
    this.stateService.logout();
  }
  
  addTodo() {
    if (this.newTodo.trim()) {
      this.stateService.addTodo(this.newTodo);
      this.newTodo = '';
    }
  }
  
  toggleTodo(id: number) {
    this.stateService.toggleTodo(id);
  }
  
  removeTodo(id: number) {
    this.stateService.removeTodo(id);
  }
  
  toggleDarkMode() {
    this.stateService.toggleDarkMode();
  }
  
  toggleSidebar() {
    this.stateService.toggleSidebar();
  }
}

3. Reactive Form with Complex Validation

// registration-form.component.ts
import { Component, OnInit } from '@angular/core';
import { 
  FormBuilder, 
  FormGroup, 
  Validators, 
  AbstractControl, 
  ValidationErrors, 
  AsyncValidatorFn 
} from '@angular/forms';
import { Observable, of } from 'rxjs';
import { map, debounceTime, switchMap, catchError, first } from 'rxjs/operators';
import { UserService } from './user.service';

@Component({
  selector: 'app-registration-form',
  template: `
    <div class="form-container">
      <h2>Registration Form</h2>
      
      <form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
        <div class="form-group">
          <label for="email">Email</label>
          <input id="email" type="email" formControlName="email">
          <div *ngIf="email?.invalid && (email?.dirty || email?.touched)" class="error">
            <div *ngIf="email?.errors?.['required']">Email is required</div>
            <div *ngIf="email?.errors?.['email']">Please enter a valid email</div>
            <div *ngIf="email?.errors?.['emailTaken']">This email is already registered</div>
          </div>
        </div>
        
        <div class="form-group">
          <label for="password">Password</label>
          <input id="password" type="password" formControlName="password">
          <div *ngIf="password?.invalid && (password?.dirty || password?.touched)" class="error">
            <div *ngIf="password?.errors?.['required']">Password is required</div>
            <div *ngIf="password?.errors?.['minlength']">Password must be at least 8 characters</div>
            <div *ngIf="password?.errors?.['passwordStrength']">
              Password must contain at least one uppercase letter, one lowercase letter, 
              one number, and one special character
            </div>
          </div>
        </div>
        
        <div class="form-group">
          <label for="confirmPassword">Confirm Password</label>
          <input id="confirmPassword" type="password" formControlName="confirmPassword">
          <div *ngIf="confirmPassword?.invalid && (confirmPassword?.dirty || confirmPassword?.touched)" class="error">
            <div *ngIf="confirmPassword?.errors?.['required']">Confirm password is required</div>
            <div *ngIf="registrationForm.errors?.['passwordMismatch']">Passwords don't match</div>
          </div>
        </div>
        
        <div formGroupName="personalInfo">
          <div class="form-group">
            <label for="firstName">First Name</label>
            <input id="firstName" type="text" formControlName="firstName">
            <div *ngIf="firstName?.invalid && (firstName?.dirty || firstName?.touched)" class="error">
              <div *ngIf="firstName?.errors?.['required']">First name is required</div>
            </div>
          </div>
          
          <div class="form-group">
            <label for="lastName">Last Name</label>
            <input id="lastName" type="text" formControlName="lastName">
            <div *ngIf="lastName?.invalid && (lastName?.dirty || lastName?.touched)" class="error">
              <div *ngIf="lastName?.errors?.['required']">Last name is required</div>
            </div>
          </div>
          
          <div class="form-group">
            <label for="age">Age</label>
            <input id="age" type="number" formControlName="age">
            <div *ngIf="age?.invalid && (age?.dirty || age?.touched)" class="error">
              <div *ngIf="age?.errors?.['required']">Age is required</div>
              <div *ngIf="age?.errors?.['min']">Age must be at least 18</div>
              <div *ngIf="age?.errors?.['max']">Age must not exceed 120</div>
            </div>
          </div>
        </div>
        
        <div class="form-group checkbox">
          <input id="terms" type="checkbox" formControlName="terms">
          <label for="terms">I agree to the Terms and Conditions</label>
          <div *ngIf="terms?.invalid && (terms?.dirty || terms?.touched)" class="error">
            <div *ngIf="terms?.errors?.['required']">You must agree to the terms</div>
          </div>
        </div>
        
        <button type="submit" [disabled]="registrationForm.invalid || registrationForm.pending">
          <span *ngIf="registrationForm.pending">Validating...</span>
          <span *ngIf="!registrationForm.pending">Register</span>
        </button>
      </form>
      
      <div *ngIf="submitted">
        <h3>Registration Successful!</h3>
        <pre>{{ formValue | json }}</pre>
      </div>
    </div>
  `,
  styles: [`
    .form-container { max-width: 500px; margin: 0 auto; padding: 20px; }
    .form-group { margin-bottom: 15px; }
    .form-group label { display: block; margin-bottom: 5px; }
    .form-group input { width: 100%; padding: 8px; border: 1px solid #ddd; }
    .checkbox { display: flex; align-items: center; gap: 10px; }
    .checkbox input { width: auto; }
    .error { color: red; font-size: 12px; margin-top: 5px; }
    button { padding: 10px 15px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
    button:disabled { background-color: #cccccc; cursor: not-allowed; }
  `]
})
export class RegistrationFormComponent implements OnInit {
  registrationForm: FormGroup;
  submitted = false;
  formValue: any;
  
  constructor(
    private fb: FormBuilder,
    private userService: UserService
  ) {
    this.registrationForm = this.fb.group({
      email: ['', {
        validators: [Validators.required, Validators.email],
        asyncValidators: [this.emailExistsValidator()]
      }],
      password: ['', [
        Validators.required, 
        Validators.minLength(8),
        this.passwordStrengthValidator()
      ]],
      confirmPassword: ['', Validators.required],
      personalInfo: this.fb.group({
        firstName: ['', Validators.required],
        lastName: ['', Validators.required],
        age: ['', [Validators.required, Validators.min(18), Validators.max(120)]]
      }),
      terms: [false, Validators.requiredTrue]
    }, { validators: this.passwordMatchValidator });
  }
  
  ngOnInit() {
    // Example of value changes subscription
    this.registrationForm.valueChanges.subscribe(value => {
      console.log('Form value changed:', value);
    });
  }
  
  // Password strength custom validator
  passwordStrengthValidator() {
    return (control: AbstractControl): ValidationErrors | null => {
      const value: string = control.value || '';
      const hasUpperCase = /[A-Z]/.test(value);
      const hasLowerCase = /[a-z]/.test(value);
      const hasNumeric = /[0-9]/.test(value);
      const hasSpecialChar = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/.test(value);
      
      const valid = hasUpperCase && hasLowerCase && hasNumeric && hasSpecialChar;
      
      return !valid ? { passwordStrength: true } : null;
    };
  }
  
  // Cross-field validator for password match
  passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
    const password = group.get('password')?.value;
    const confirmPassword = group.get('confirmPassword')?.value;
    
    return password === confirmPassword ? null : { passwordMismatch: true };
  }
  
  // Async validator for email uniqueness
  emailExistsValidator(): AsyncValidatorFn {
    return (control: AbstractControl): Observable<ValidationErrors | null> => {
      const email = control.value;
      
      if (!email) {
        return of(null);
      }
      
      return of(email).pipe(
        debounceTime(400),
        switchMap(email => 
          this.userService.checkEmailExists(email).pipe(
            map(exists => exists ? { emailTaken: true } : null),
            catchError(() => of(null))
          )
        ),
        first() // Complete after first emission
      );
    };
  }
  
  // Getter methods for form controls (to simplify template code)
  get email() { return this.registrationForm.get('email'); }
  get password() { return this.registrationForm.get('password'); }
  get confirmPassword() { return this.registrationForm.get('confirmPassword'); }
  get firstName() { return this.registrationForm.get('personalInfo.firstName'); }
  get lastName() { return this.registrationForm.get('personalInfo.lastName'); }
  get age() { return this.registrationForm.get('personalInfo.age'); }
  get terms() { return this.registrationForm.get('terms'); }
  
  onSubmit() {
    if (this.registrationForm.valid) {
      this.submitted = true;
      this.formValue = this.registrationForm.value;
      
      // In a real app, you would call a service to submit the form
      console.log('Form submitted successfully', this.formValue);
    } else {
      // Mark all fields as touched to trigger validation messages
      this.markFormGroupTouched(this.registrationForm);
    }
  }
  
  // Helper method to mark all controls in a form group as touched
  markFormGroupTouched(formGroup: FormGroup) {
    Object.values(formGroup.controls).forEach(control => {
      control.markAsTouched();
      
      if (control instanceof FormGroup) {
        this.markFormGroupTouched(control);
      }
    });
  }
}

// Mock UserService for async validation
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private existingEmails = ['test@example.com', 'admin@example.com', 'user@example.com'];
  
  checkEmailExists(email: string): Observable<boolean> {
    // Simulate API call with delay
    return of(this.existingEmails.includes(email)).pipe(delay(1000));
  }
}

Performance Optimization

4. OnPush Change Detection Strategy

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { ParentComponent } from './parent.component';
import { ChildComponent } from './child.component';

@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent, ParentComponent, ChildComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }
// parent.component.ts
import { Component } from '@angular/core';

export interface User {
  id: number;
  name: string;
  email: string;
}

@Component({
  selector: 'app-parent',
  template: `
    <div class="parent-container">
      <h2>Parent Component (Default Change Detection)</h2>
      <p>Counter: {{ counter }}</p>
      <p>Last Update: {{ lastUpdate }}</p>
      
      <div class="controls">
        <button (click)="incrementCounter()">Increment Counter</button>
        <button (click)="updateUserObject()">Update User Object</button>
        <button (click)="updateUserProperty()">Update User Property</button>
        <button (click)="pushToArray()">Add Item to Array</button>
        <button (click)="updateArrayReference()">Create New Array</button>
      </div>
      
      <div class="user-input">
        <input [(ngModel)]="newName" placeholder="New user name">
        <button (click)="updateName()">Update Name</button>
      </div>
      
      <div class="child-components">
        <app-child 
          [counter]="counter"
          [user]="user"
          [items]="items"
          (update)="onChildUpdate($event)">
        </app-child>
      </div>
    </div>
  `,
  styles: [`
    .parent-container { padding: 20px; border: 2px solid blue; margin: 10px; }
    .controls { margin: 15px 0; display: flex; gap: 10px; }
    .user-input { margin: 15px 0; }
    .child-components { margin-top: 20px; }
  `]
})
export class ParentComponent {
  counter = 0;
  lastUpdate = new Date().toLocaleTimeString();
  user: User = { id: 1, name: 'John Doe', email: 'john@example.com' };
  items: string[] = ['Item 1', 'Item 2', 'Item 3'];
  newName = '';
  
  incrementCounter() {
    this.counter++;
    this.updateTimestamp();
  }
  
  updateUserObject() {
    // Creates a new reference (triggers OnPush detection)
    this.user = { ...this.user };
    this.updateTimestamp();
  }
  
  updateUserProperty() {
    // Mutates the existing object (doesn't trigger OnPush unless marked)
    this.user.name = 'Jane Doe';
    this.updateTimestamp();
  }
  
  pushToArray() {
    // Mutates the array (doesn't trigger OnPush unless marked)
    this.items.push(`Item ${this.items.length + 1}`);
    this.updateTimestamp();
  }
  
  updateArrayReference() {
    // Creates a new reference (triggers OnPush detection)
    this.items = [...this.items, `Item ${this.items.length + 1}`];
    this.updateTimestamp();
  }
  
  updateName() {
    if (this.newName) {
      // Creates a new reference (triggers OnPush detection)
      this.user = { ...this.user, name: this.newName };
      this.newName = '';
      this.updateTimestamp();
    }
  }
  
  onChildUpdate(message: string) {
    console.log('Message from child:', message);
    this.updateTimestamp();
  }
  
  updateTimestamp() {
    this.lastUpdate = new Date().toLocaleTimeString();
  }
}
// child.component.ts
import { 
  Component, 
  Input, 
  Output, 
  EventEmitter, 
  ChangeDetectionStrategy, 
  ChangeDetectorRef,
  OnInit,
  OnChanges,
  SimpleChanges
} from '@angular/core';
import { User } from './parent.component';

@Component({
  selector: 'app-child',
  template: `
    <div class="child-container">
      <h3>Child Component (OnPush Strategy)</h3>
      <p>Counter from parent: {{ counter }}</p>
      <p>User: {{ user.name }} ({{ user.email }})</p>
      <p>Items count: {{ items.length }}</p>
      <p>Local counter: {{ localCounter }}</p>
      <p>Last render: {{ lastRender }}</p>
      
      <ul>
        <li *ngFor="let item of items">{{ item }}</li>
      </ul>
      
      <div class="controls">
        <button (click)="incrementLocal()">Increment Local Counter</button>
        <button (click)="emitEvent()">Emit Event</button>
        <button (click)="forceDetection()">Force Detection</button>
      </div>
    </div>
  `,
  styles: [`
    .child-container { padding: 15px; border: 2px solid green; margin: 10px; }
    .controls { margin-top: 15px; display: flex; gap: 10px; }
  `],
  // Use OnPush change detection strategy
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit, OnChanges {
  @Input() counter = 0;
  @Input() user!: User;
  @Input() items!: string[];
  @Output() update = new EventEmitter<string>();
  
  localCounter = 0;
  lastRender = new Date().toLocaleTimeString();
  
  constructor(private cd: ChangeDetectorRef) {}
  
  ngOnInit() {
    // Example of marking for check after an async operation
    setTimeout(() => {
      this.localCounter = 100;
      // Need to manually trigger change detection since we're using OnPush
      this.cd.markForCheck();
    }, 3000);
  }
  
  ngOnChanges(changes: SimpleChanges) {
    console.log('Child received changes:', changes);
    this.updateTimestamp();
    
    // Log which input triggered change detection
    if (changes['counter']) {
      console.log('Counter changed to:', this.counter);
    }
    
    if (changes['user']) {
      console.log('User reference changed:', this.user);
      
      // Check if it's a reference change or initial change
      if (!changes['user'].firstChange) {
        console.log('Previous user:', changes['user'].previousValue);
        console.log('Current user:', changes['user'].currentValue);
      }
    }
    
    if (changes['items']) {
      console.log('Items array reference changed:', this.items);
    }
  }
  
  incrementLocal() {
    this.localCounter++;
    this.updateTimestamp();
    // Need to manually trigger change detection since we're using OnPush
    this.cd.markForCheck();
  }
  
  emitEvent() {
    this.update.emit(`Event from child at ${new Date().toLocaleTimeString()}`);
    // No need to call markForCheck() here since Angular automatically does this
    // when an @Output event is emitted
  }
  
  forceDetection() {
    this.updateTimestamp();
    // Force change detection
    this.cd.detectChanges();
  }
  
  updateTimestamp() {
    this.lastRender = new Date().toLocaleTimeString();
  }
}

5. Virtual Scrolling

// app.module.ts
import { NgModule } from '@angular/core

Angular URL serializer to properly handle URLs that start with query parameters directly (without a path adding trailing slashes Angular

 Example Code

The key requirement:


In the parse method, I added special handling for URLs that start with query parameters:


If the URL starts with '?', I prepend a '/' so Angular can properly parse it

This allows Angular to handle URLs like ?param1=value1&param2=value2


In the serialize method, I added special handling for query-only URLs:


If the URL starts with '/?', I remove the leading slash

This converts Angular's internal representation back to the expected query-only format



This implementation properly handles these types of URLs:

  • ?param1=value1&param2=value2&param3=value3 (query-only URL)
  • /path?param1=value1&param2=value2 (path with query parameters)
  • /path/?param1=value1&param2=value2 (path with trailing slash and query parameters)


import { DefaultUrlSerializer, UrlTree } from '@angular/router';


export class TrailingSlashSerializer extends DefaultUrlSerializer {

  // Remove trailing slashes during ROUTE MATCHING

  parse(url: string): UrlTree {

    // Special handling for URLs that start with query parameters

    if (url.startsWith('?')) {

      return super.parse('/' + url);

    }

    

    // Normal case: Remove ALL trailing slashes for clean route matching

    const cleaned = url.replace(/\/+$/g, '') || '/';

    return super.parse(cleaned);

  }


  // Add trailing slashes during URL GENERATION

  serialize(tree: UrlTree): string {

    const defaultUrl = super.serialize(tree);

    

    // Handle URLs that are just query parameters (no path)

    if (defaultUrl.startsWith('/?')) {

      return defaultUrl.substring(1); // Remove the leading slash

    }

    

    // Split the URL into parts: path and query/fragment

    const [path, ...rest] = defaultUrl.split(/(?=\?|#)/);

    const queryOrFragment = rest.join(''); // Rejoin any query/fragment parts

    

    // Case 1: Root URL - keep as "/"

    if (path === '/') return '/' + queryOrFragment;

    

    // Case 2: .html files (SSG) - leave untouched

    if (path.endsWith('.html')) return path + queryOrFragment;

    

    // Case 3: Add exactly one trailing slash if not already present

    const modifiedPath = path.endsWith('/') ? path : `${path}/`;

    

    return modifiedPath + queryOrFragment;

  }

}


Example 2  


// trailing-slash-serializer.ts

import { DefaultUrlSerializer, UrlTree } from '@angular/router';


export class TrailingSlashSerializer extends DefaultUrlSerializer {

  parse(url: string): UrlTree {

    // Remove trailing slashes for route matching

    const cleaned = url.replace(/\/+$/g, '') || '/';

    return super.parse(cleaned);

  }


  serialize(tree: UrlTree): string {

    const defaultUrl = super.serialize(tree);

    const [path, queryOrFragment] = defaultUrl.split(/(?=[?#])/);


    // Handle root and .html files

    if (path === '/' || path.endsWith('.html')) {

      return defaultUrl;

    }


    // Add exactly one trailing slash

    const modifiedPath = path.endsWith('/') ? path : `${path}/`;

    return modifiedPath + (queryOrFragment || '');

  }

}

C# .Net Fundamentals interview questions

 

1. Basic .NET Concepts

  1. What is the .NET Framework?

    • A software framework by Microsoft that provides a runtime (CLR) and libraries for building applications.

  2. What is CLR (Common Language Runtime)?

    • The execution engine that manages memory, security, and exception handling for .NET applications.

  3. What is CTS (Common Type System) and CLS (Common Language Specification)?

    • CTS defines data types across .NET languages.

    • CLS ensures language interoperability by defining rules.

  4. What is Managed vs Unmanaged Code?

    • Managed code runs under CLR control (e.g., C#).

    • Unmanaged code runs outside CLR (e.g., C++ native code).


2. C# Fundamentals

  1. What are the main differences between C# and Java?

    • C# has properties, delegates, events, and structs (value types), while Java relies more on interfaces.

  2. What is boxing and unboxing?

    • Boxing: Converting a value type to an object (e.g., int to object).

    • Unboxing: Converting an object back to a value type.

  3. What is the difference between == and .Equals()?

    • == compares references (for objects) or values (for primitives).

    • .Equals() compares content (can be overridden).

  4. What are value types vs reference types?

    • Value types (intstruct) store data directly.

    • Reference types (classstring) store memory references.


3. OOP Concepts

  1. What are the four pillars of OOP?

    • EncapsulationInheritancePolymorphismAbstraction.

  2. What is the difference between abstract class and interface?

    • Abstract class can have method implementations; interface cannot (until C# 8.0).

    • A class can inherit only one abstract class but multiple interfaces.

  3. What is method overloading vs overriding?

    • Overloading: Same method name, different parameters (compile-time).

    • Overriding: Redefining a method in a derived class (runtime, using virtual/override).


4. Memory Management & Garbage Collection

  1. What is Garbage Collection (GC) in .NET?

    • Automatic memory management by CLR that reclaims unused objects.

  2. What are generations in GC (Gen 0, Gen 1, Gen 2)?

    • Objects are promoted through generations based on lifespan (Gen 0 = short-lived, Gen 2 = long-lived).

  3. What is the IDisposable interface?

    • Used to release unmanaged resources (e.g., file handles) via the Dispose() method.

  4. What is the using statement in C#?

    • Ensures Dispose() is called automatically (syntactic sugar for try-finally).


5. Asynchronous Programming

  1. What is the difference between Task and Thread?

    • Thread is a low-level OS construct.

    • Task is a higher-level abstraction for async operations (uses thread pool).

  2. What are async and await?

    • async marks a method as asynchronous.

    • await pauses execution until the task completes.

  3. What is deadlock in async programming?

    • Occurs when two tasks wait indefinitely for each other (e.g., due to .Result or .Wait() misuse).


6. ASP.NET & Web APIs

  1. What is the difference between ASP.NET MVC and Web API?

    • MVC returns views (HTML), Web API returns data (JSON/XML).

  2. What is Middleware in ASP.NET Core?

    • Components that handle requests/responses (e.g., authentication, logging).

  3. What is Dependency Injection (DI)?

    • A design pattern where dependencies are injected rather than created inside a class.

  4. What is JWT (JSON Web Token)?

    • A token-based authentication mechanism used in APIs.


7. Entity Framework (ORM)

  1. What is Entity Framework?

    • An ORM (Object-Relational Mapper) for database operations using C# objects.

  2. What is the difference between IQueryable and IEnumerable?

    • IQueryable executes queries on the database.

    • IEnumerable executes queries in memory.

  3. What are LINQ and its types (Query vs Method Syntax)?

    • LINQ (Language Integrated Query) allows querying collections.

    • Query Syntax: SQL-like (e.g., from x in list select x).

    • Method Syntax: Fluent (e.g., list.Where(x => x > 5)).


8. Advanced Topics

  1. What is Reflection in .NET?

    • Inspecting types/metadata at runtime (e.g., typeof()GetMethod()).

  2. What is a Delegate and an Event?

    • Delegate: A type-safe function pointer.

    • Event: A mechanism for notification (uses delegates).

  3. What is Dependency Injection (DI) in .NET Core?

    • Built-in IoC container for managing object lifetimes (e.g., AddScopedAddSingleton).

  4. What is SignalR?

    • A library for real-time web functionality (e.g., chat apps).

  5. What is .NET Core vs .NET Framework vs .NET 5/6/7/8?

    • .NET Framework: Windows-only.

    • .NET Core: Cross-platform, open-source.

    • .NET 5+: Unified platform (successor to both).


Final Tips

  • Be ready for coding challenges (e.g., reverse a string, Fibonacci series).

  • Know design patterns (Singleton, Factory, Repository).

  • Understand REST principles if applying for API roles.

  • Practice Entity Framework queries and performance tuning.

Advanced Angular Interview Questions with Code Examples

Advanced Angular Interview Questions with Code Examples Component Communication & State Management 1. Parent-Child Communication with ...

Best for you