Curated questions covering components, directives, services, RxJS, routing, forms, signals, change detection, and Angular CLI.
Angular is a TypeScript-based open-source front-end framework by Google. Key features: component-based architecture, two-way data binding, dependency injection, RxJS-based reactivity, Angular CLI, built-in routing, reactive and template-driven forms, and ahead-of-time (AOT) compilation.
Components are the building blocks of Angular applications. Each component has a TypeScript class decorated with @Component, an HTML template, and optional CSS styles. Components control a portion of the UI and communicate via @Input() and @Output().
@Component({\n selector: "app-user",\n template: `<h1>Hello, {{ name }}</h1>`,\n styles: [`h1 { color: red; }`]\n})\nexport class UserComponent {\n @Input() name = "";\n}
Dependency Injection (DI) is a design pattern where Angular provides dependencies to components and services rather than having them create their own. Use the inject() function or constructor injection. Services are registered in providers arrays or with providedIn.
@Injectable({ providedIn: "root" })\nexport class UserService {\n getUsers() { return []; }\n}\n\n@Component({ ... })\nexport class AppComponent {\n private userService = inject(UserService);\n}
// Reactive form\nthis.form = this.fb.group({\n email: ["", [Validators.required, Validators.email]],\n password: ["", Validators.minLength(8)]\n});
Change detection is the mechanism Angular uses to sync the component tree with the data model. Default strategy checks every component on every event. OnPush strategy only checks when an @Input() reference changes, an event originates from the component, or async pipe emits.
@Component({\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserComponent {}
Signals are a reactive primitive introduced in Angular 16+. signal() creates a reactive state, computed() derives values, effect() runs side effects. Signals enable fine-grained reactivity without Zone.js.
import { signal, computed, effect } from "@angular/core";\n\nconst count = signal(0);\nconst doubled = computed(() => count() * 2);\n\neffect(() => console.log("Count:", count()));\n\ncount.set(5); // set\ncount.update(n => n + 1); // update
RxJS is a library for reactive programming using Observables. Key operators:
// Search: cancel previous request on new keystroke\nthis.searchInput.valueChanges.pipe(\n debounceTime(300),\n switchMap(term => this.api.search(term))\n).subscribe();
The Angular Router enables navigation between views. Configure routes with provideRouter() or RouterModule.forRoot(). Use routerLink for navigation, ActivatedRoute for params, and Router service for programmatic navigation.
const routes: Routes = [\n { path: "", component: HomeComponent },\n { path: "users/:id", component: UserComponent },\n { path: "admin", loadChildren: () => import("./admin/admin.routes") },\n { path: "**", redirectTo: "" }\n];
Lazy loading loads feature modules only when the user navigates to them, reducing the initial bundle size. Use loadChildren with dynamic import() in route config. Angular 17+ uses standalone components with loadComponent.
{ path: "admin", loadChildren: () =>\n import("./admin/admin.module").then(m => m.AdminModule) }\n\n// Standalone (Angular 17+)\n{ path: "admin", loadComponent: () =>\n import("./admin.component").then(c => c.AdminComponent) }
Guards control navigation. Types: CanActivate (can user enter route?), CanDeactivate (can user leave?), CanActivateChild (child routes), CanMatch (route matching), Resolve (pre-fetch data). Modern Angular uses functional guards.
// Functional guard (Angular 15+)\nexport const authGuard: CanActivateFn = (route, state) => {\n const auth = inject(AuthService);\n return auth.isLoggedIn() ? true : inject(Router).createUrlTree(["/login"]);\n};
Lifecycle hooks let you tap into key moments of a component/directive lifecycle.
The async pipe subscribes to an Observable or Promise in the template and automatically unsubscribes when the component is destroyed. It eliminates manual subscription management and memory leaks.
// Component\nusers$ = this.userService.getUsers();\n\n// Template\n<ul>\n <li *ngFor="let user of users$ | async">{{ user.name }}</li>\n</ul>
@ViewChild("myInput") inputRef!: ElementRef;\n@ContentChild(HeaderComponent) header!: HeaderComponent;
Content projection allows a component to accept and display external HTML content via
<!-- card.component.html -->\n<div class="tl-card">\n <ng-content select="[card-title]"></ng-content>\n <ng-content></ng-content>\n</div>\n\n<!-- Usage -->\n<app-card>\n <h2 card-title>Title</h2>\n <p>Body content</p>\n</app-card>
const bs = new BehaviorSubject<number>(0);\nbs.subscribe(v => console.log("A:", v)); // A: 0\nbs.next(1); // A: 1\nbs.subscribe(v => console.log("B:", v)); // B: 1 (gets current value)
Standalone components (Angular 14+) do not need to be declared in an NgModule. They import their own dependencies directly. Angular 17+ makes standalone the default. Use bootstrapApplication() instead of AppModule.
@Component({\n standalone: true,\n imports: [CommonModule, RouterModule, FormsModule],\n template: `<h1>Hello</h1>`\n})\nexport class AppComponent {}
HttpClient provides methods for HTTP requests (get, post, put, delete). It returns Observables. Handle errors with catchError operator and throwError.
this.http.get<User[]>("/api/users").pipe(\n catchError(err => {\n console.error(err);\n return throwError(() => new Error("Failed to load users"));\n })\n).subscribe(users => this.users = users);
@Pipe({ name: "filter", pure: false }) // impure\nexport class FilterPipe implements PipeTransform {\n transform(items: any[], term: string) {\n return items.filter(i => i.name.includes(term));\n }\n}
Zone.js patches browser APIs (setTimeout, Promises, events) to notify Angular when async operations complete, triggering change detection. Zoneless Angular (Angular 18+) removes this dependency, using Signals for explicit change notification. Zoneless apps are faster and easier to debug.
<div [ngSwitch]="status">\n <p *ngSwitchCase="'active'">Active</p>\n <p *ngSwitchCase="'inactive'">Inactive</p>\n <p *ngSwitchDefault>Unknown</p>\n</div>
@defer (Angular 17+) lazily loads a block of template content and its dependencies. Supports triggers: on idle, on viewport, on interaction, on hover, on timer, when condition. Replaces manual lazy loading patterns.
@defer (on viewport) {\n <app-heavy-chart />\n} @placeholder {\n <p>Loading chart...</p>\n} @loading (minimum 500ms) {\n <app-spinner />\n} @error {\n <p>Failed to load</p>\n}
@Input() marks a property as an input binding from a parent component. @Input({ required: true }) (Angular 16+) makes the input mandatory — Angular throws a compile-time error if the parent does not provide it, eliminating runtime undefined bugs.
@Component({ ... })\nexport class CardComponent {\n @Input({ required: true }) title!: string;\n @Input() subtitle = ""; // optional with default\n}
The Angular Component Dev Kit (CDK) provides low-level primitives for building UI components: Overlay (popups, tooltips), DragDrop, VirtualScrolling, Accessibility (focus trap, live announcer), Portal, and Layout (breakpoint observer). It is the foundation of Angular Material.
A Resolver pre-fetches data before a route is activated, ensuring the component has the data it needs on initialization. Modern Angular uses functional resolvers.
export const userResolver: ResolveFn<User> = (route) => {\n return inject(UserService).getUser(route.paramMap.get("id")!);\n};\n\n// Route config\n{ path: "user/:id", component: UserComponent, resolve: { user: userResolver } }
takeUntilDestroyed (Angular 16+) automatically completes an Observable when the component is destroyed. It replaces the manual takeUntil + Subject pattern for unsubscribing.
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";\n\nexport class UserComponent {\n constructor() {\n this.userService.getUsers().pipe(\n takeUntilDestroyed() // auto-unsubscribes on destroy\n ).subscribe(users => this.users = users);\n }\n}
constructor(private renderer: Renderer2, private el: ElementRef) {}\n\nngOnInit() {\n this.renderer.setStyle(this.el.nativeElement, "color", "red");\n}
TestBed is Angular's primary testing utility. It creates a testing module that mimics an NgModule, allowing you to test components, services, and pipes in isolation with dependency injection.
describe("UserComponent", () => {\n beforeEach(() => TestBed.configureTestingModule({\n imports: [UserComponent],\n providers: [{ provide: UserService, useValue: mockUserService }]\n }));\n\n it("should display name", () => {\n const fixture = TestBed.createComponent(UserComponent);\n fixture.detectChanges();\n expect(fixture.nativeElement.textContent).toContain("Alice");\n });\n});
@Directive({ selector: "[appHighlight]" })\nexport class HighlightDirective {\n @HostBinding("style.backgroundColor") bgColor = "";\n\n @HostListener("mouseenter") onEnter() { this.bgColor = "yellow"; }\n @HostListener("mouseleave") onLeave() { this.bgColor = ""; }\n}
inject() is a function-based alternative to constructor injection. It can be used in component/directive/pipe constructors, factory functions, and guards. It enables cleaner code and works in standalone contexts without constructor boilerplate.
@Component({ ... })\nexport class UserComponent {\n private userService = inject(UserService);\n private router = inject(Router);\n private route = inject(ActivatedRoute);\n}
// Template-driven\n<input [(ngModel)]="username">\n\n// Reactive\n<input [formControl]="usernameControl">\nusernameControl = new FormControl("", Validators.required);
Angular animations use the Web Animations API via @angular/animations. Define animations with trigger(), state(), transition(), animate(), and keyframes(). Import BrowserAnimationsModule or provideAnimations().
@Component({\n animations: [\n trigger("fadeIn", [\n transition(":enter", [\n style({ opacity: 0 }),\n animate("300ms ease-in", style({ opacity: 1 }))\n ])\n ])\n ]\n})\nexport class CardComponent {}
Interceptors intercept HTTP requests and responses globally. Used for adding auth headers, logging, error handling, and loading indicators.
export const authInterceptor: HttpInterceptorFn = (req, next) => {\n const token = inject(AuthService).getToken();\n const authReq = req.clone({\n headers: req.headers.set("Authorization", `Bearer ${token}`)\n });\n return next(authReq);\n};
ActivatedRoute provides information about the currently activated route: params, queryParams, data, fragment, and URL segments. Use snapshot for one-time reads or subscribe to the Observable for dynamic changes.
export class UserComponent {\n private route = inject(ActivatedRoute);\n\n ngOnInit() {\n // Snapshot (one-time)\n const id = this.route.snapshot.paramMap.get("id");\n\n // Observable (reacts to changes)\n this.route.paramMap.subscribe(params => {\n this.loadUser(params.get("id"));\n });\n }\n}
// Parallel HTTP requests\nforkJoin([this.api.getUser(), this.api.getPosts()])\n .subscribe(([user, posts]) => { ... });
FormArray manages a dynamic list of FormControls or FormGroups. Use it for forms with a variable number of fields (e.g., adding/removing phone numbers).
this.form = this.fb.group({\n phones: this.fb.array([\n this.fb.control("", Validators.required)\n ])\n});\n\nget phones() { return this.form.get("phones") as FormArray; }\n\naddPhone() {\n this.phones.push(this.fb.control(""));\n}
@Component({\n encapsulation: ViewEncapsulation.None // global styles\n})
toSignal() converts an Observable to a Signal for use in signal-based components. toObservable() converts a Signal back to an Observable. Both are in @angular/core/rxjs-interop.
import { toSignal, toObservable } from "@angular/core/rxjs-interop";\n\nexport class UserComponent {\n users = toSignal(this.userService.getUsers(), { initialValue: [] });\n // users is now a Signal<User[]>, usable in templates without async pipe\n}
By default, *ngFor tracks items by object identity — any change recreates all DOM elements. trackBy provides a function returning a unique identifier, so Angular only re-renders changed items. Critical for performance with large lists.
<li *ngFor="let user of users; trackBy: trackById">{{ user.name }}</li>\n\ntrackById(index: number, user: User): number {\n return user.id;\n}
Angular uses environment files (environment.ts, environment.prod.ts) to store build-specific configuration like API URLs and feature flags. Angular CLI replaces the file at build time using fileReplacements in angular.json.
// environment.ts\nexport const environment = {\n production: false,\n apiUrl: "http://localhost:3000"\n};\n\n// Usage\nimport { environment } from "../environments/environment";\nthis.http.get(environment.apiUrl + "/users");
// Traditional\n@Output() userSelected = new EventEmitter<User>();\n\n// Signal-based (Angular 17.3+)\nuserSelected = output<User>();
Explore 500+ free tutorials across 20+ languages and frameworks.