Angular Interview Questions & Answers¶
A comprehensive Q&A reference for Angular developers — from beginner to advanced. Includes theory, code examples, and real-world use cases.
ng new my-angular-app -> Create a New Angular App
🅰 Angular 17 Topics¶
🔹 New Features (Angular 17)¶
- Signals (reactive state management)
- Improved control flow (
@if,@for) - deferrable views (@defer)
- Standalone components
- Better performance
***¶
which design pattern you use in your Angular project¶
"In my Angular projects, I primarily use the Component-based architecture that Angular provides. For business logic and API communication, I follow the Service pattern with Dependency Injection. For state management, I use Angular Signals (or NgRx if applicable), which follows reactive and Observer patterns. I also use the Facade pattern to keep components independent of the state management implementation. For data access, I separate API calls into services or repository-like layers. Additionally, I use Route Guards for authorization and Smart/Presentational component separation to improve maintainability."
Table of Contents¶
- Angular Core Fundamentals
- 1.1 Default Libraries in Angular
- 1.2 zone.js and webpack
- 1.3 angular.json
- 1.4 Components
- 1.5 Component Decorator
- 1.6 Rendering a Component
- 1.7 Standalone Components
- 1.8 Single File Components
- Lifecycle Events
- 2.1 What are Lifecycle Events?
- 2.2 Types of Lifecycle Events
- 2.3 Most Used Events and Use Cases
- 2.4 Constructor vs Lifecycle Events
- Data Binding
- 3.1 One-Way Data Binding
- 3.2 Two-Way Data Binding
- Directives
- 4.1 Structural Directives
- 4.2 Attribute Directives
- Control Flow Statements
- Signals
- 6.1 What is a Signal?
- 6.2 Creating a Signal
- 6.3 Accessing a Signal
- 6.4 Updating a Signal
- 6.5 Signals and Change Detection
- Linked Signal
- Routing
- 8.1 What is Routing?
- 8.2 Creating Routes
- 8.3 router-outlet
- 8.4 Programmatic Navigation
- 8.5 routerLink
- Pipes
- Resource API
- Component Communication
- Angular CLI — Global vs Local Install
- RxJS
- 13.1 What is RxJS?
- 13.2 What is an Observable?
- 13.3 Promise vs Observable
- 13.4 Why provideHttpClient at App Level?
- Route Guards
- JWT Security Best Practices
1. Angular Core Fundamentals¶
1.1 What are the default libraries installed with Angular and what is their unique purpose?¶
When you run ng new, Angular installs the following core libraries:
| Library | Purpose |
|---|---|
@angular/core | Core decorators, DI system, component engine |
@angular/common | Built-in directives (NgIf, NgFor), pipes (DatePipe, etc.) |
@angular/forms | Template-driven and reactive forms |
@angular/router | Client-side routing and navigation |
@angular/platform-browser | DOM rendering in browser environment |
@angular/platform-browser-dynamic | JIT compilation support |
@angular/animations | Animation support for components |
@angular/compiler | Compiles templates at build time (AOT) |
rxjs | Reactive programming — powers HttpClient, Router, Forms |
zone.js | Tracks async tasks to trigger change detection |
tslib | TypeScript runtime helpers |
1.2 What is the role of zone.js and webpack in Angular?¶
zone.js¶
Theory: zone.js is a library that patches all async APIs in JavaScript (setTimeout, Promise, XHR, event listeners, etc.) so Angular knows when async operations complete and when to run change detection.
User clicks button
→ event fires inside Zone
→ zone.js patches the event
→ Angular gets notified
→ Change detection runs
→ DOM updates
Real Use Case: Without zone.js, if you do setTimeout(() => this.name = 'John', 1000), Angular wouldn't know to re-render. zone.js intercepts the timer and triggers a check.
🔑 Signals (Angular 17+) are designed to eventually remove the need for zone.js — components can use
ChangeDetectionStrategy.OnPushand signals to be fully zone-free.
webpack¶
Theory: webpack is the module bundler Angular uses internally (via @angular-devkit). It:
- Bundles all TypeScript, CSS, and HTML files into optimized JS bundles
- Tree-shakes unused code
- Handles code splitting for lazy-loaded routes
- Generates
main.js,polyfills.js,styles.cssin thedist/folder
In Angular 17+, esbuild is the default builder (faster), but webpack is still supported.
1.3 What is angular.json and what are its key options?¶
Theory: angular.json is the workspace configuration file for the Angular CLI. It defines how the project is built, served, and tested.
Key sections:
{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": ["src/assets"],
"styles": ["src/styles.css"],
"scripts": [],
"outputPath": "dist/my-app"
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.production.ts"
}
],
"optimization": true
}
}
}
}
}
}
}
index.html — The shell HTML page. Angular injects <app-root> here and the CLI injects script tags automatically.
main.ts — The JavaScript entry point that bootstraps the Angular application:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig);
1.4 What is a Component and how do you create one?¶
Theory: A component is the fundamental building block of an Angular application. Every component has:
- A TypeScript class (logic)
- An HTML template (view)
- CSS styles (appearance)
Creating via CLI (recommended):
ng generate component pages/user-profile
# shorthand:
ng g c pages/user-profile
Creating manually:
import { Component } from '@angular/core';
@Component({
selector: 'app-user-profile',
standalone: true,
templateUrl: './user-profile.component.html',
styleUrl: './user-profile.component.css'
})
export class UserProfileComponent {
username = 'John Doe';
}
Real Use Case: Break the app into reusable pieces — HeaderComponent, FooterComponent, UserCardComponent — each responsible for one thing.
1.5 What is the Component Decorator?¶
🔹 Decorators¶
-
Decorators are special TypeScript functions that provide metadata to Angular classes, properties, methods, or parameters. They tell Angular how a class or member should be processed.
-
@Component-> Defines a component. @Injectable-> Marks a class as available for dependency injection.@Directive- @Input – Receives data from a parent component.
- @Output – Sends data/events to a parent component. @Pipe – Defines a pipe.
Theory: The @Component decorator is a metadata function that tells Angular how to process and render a class as a component.
@Component({
selector: 'app-hero',
standalone: true,
imports: [CommonModule],
template: `<h1>{{ title }}</h1>`,
styles: [`h1 { color: red; }`],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeroComponent {
title = 'Superman';
}
| Property | Purpose |
|---|---|
selector | How the component is used in HTML |
standalone | If true, no NgModule required |
templateUrl | Path to external HTML file |
styleUrl | Path to external CSS file |
imports | Dependencies needed by the template |
changeDetection | When Angular checks for changes |
1.6 How do you render a Component?¶
Method 1 — In a template:
<!-- parent.component.html -->
<app-hero></app-hero>
<app-user-profile [userId]="123"></app-user-profile>
Don't forget to add it to the parent's imports array:
@Component({
imports: [HeroComponent, UserProfileComponent],
...
})
export class ParentComponent {}
Method 2 — Via routing:
{ path: 'hero', component: HeroComponent }
<!-- layout.html -->
<router-outlet></router-outlet>
1.7 What is a Standalone Component?¶
Theory: A standalone component does not belong to any NgModule. It manages its own dependencies via the imports array in @Component. This is the default in Angular 17+.
Before (Module-based):
@NgModule({
declarations: [HeroComponent],
imports: [CommonModule],
})
export class HeroModule {}
After (Standalone — Angular 17+):
@Component({
selector: 'app-hero',
standalone: true,
imports: [CommonModule],
template: `...`
})
export class HeroComponent {}
Why standalone?
- Simpler — no need to maintain NgModules
- Better tree-shaking (only imports what's actually used)
- Easier lazy loading per component
1.8 How do you create a single file component and when should you use it?¶
Theory: A single file component has the template and styles inline inside the .ts file.
@Component({
selector: 'app-badge',
standalone: true,
template: `
<span class="badge" [class]="color">{{ label }}</span>
`,
styles: [`
.badge { padding: 4px 8px; border-radius: 4px; color: white; }
.primary { background: blue; }
.danger { background: red; }
`]
})
export class BadgeComponent {
label = 'New';
color = 'primary';
}
When to use:
| Use single file | Use separate files |
|---|---|
| Small, simple UI pieces | Complex templates (>20 lines) |
| Reusable widgets (badge, spinner, avatar) | Components with heavy styling |
| Prototyping / demos | Production feature components |
2. Lifecycle Events¶
2.1 What are Lifecycle Events?¶
Theory: Lifecycle hooks are methods Angular calls automatically at key moments during a component's life — from creation to destruction.
Constructor → ngOnChanges → ngOnInit → ngDoCheck
→ ngAfterContentInit → ngAfterContentChecked
→ ngAfterViewInit → ngAfterViewChecked
→ (repeat on changes)
→ ngOnDestroy
export class MyComponent implements OnInit, OnDestroy {
ngOnInit() { /* runs once after component is created */ }
ngOnDestroy() { /* runs before component is destroyed */ }
}
2.2 What are the types of Lifecycle Events?¶
| Hook | When it runs | Common Use |
|---|---|---|
ngOnChanges | When @Input values change | React to parent data changes |
ngOnInit | Once after first ngOnChanges | API calls, form init |
ngDoCheck | Every change detection cycle | Custom change detection |
ngAfterContentInit | After <ng-content> is projected | Access projected content |
ngAfterContentChecked | After projected content is checked | Respond to content changes |
ngAfterViewInit | After component's view is rendered | DOM access, @ViewChild |
ngAfterViewChecked | After view and children are checked | Performance-sensitive updates |
ngOnDestroy | Just before component is removed | Cleanup subscriptions, timers |
ngOnInit — API call example:
ngOnInit(): void {
this.userService.getUser(this.userId).subscribe(user => {
this.user = user;
});
}
ngAfterViewInit — DOM access:
@ViewChild('myCanvas') canvasRef!: ElementRef;
ngAfterViewInit(): void {
// Safe to access DOM here — view is fully rendered
const ctx = this.canvasRef.nativeElement.getContext('2d');
ctx.fillRect(10, 10, 100, 100);
}
ngOnDestroy — Cleanup:
private subscription!: Subscription;
ngOnInit() {
this.subscription = this.dataService.data$.subscribe(d => this.data = d);
}
ngOnDestroy() {
this.subscription.unsubscribe(); // Prevent memory leaks
clearInterval(this.timer);
}
2.3 What are the most used Lifecycle Events and their use cases?¶
| Hook | Most Common Use Case |
|---|---|
ngOnInit | Fetch initial data from API, initialize reactive forms |
ngOnChanges | Recompute derived values when @Input changes |
ngAfterViewInit | Access @ViewChild, initialize third-party DOM libraries |
ngOnDestroy | Unsubscribe Observables, clear timers, detach event listeners |
Real Use Case — Auto-refreshing dashboard:
export class DashboardComponent implements OnInit, OnDestroy {
stats: Stats[] = [];
private autoRefresh!: Subscription;
ngOnInit(): void {
this.loadStats();
this.autoRefresh = interval(30000).subscribe(() => this.loadStats());
}
private loadStats(): void {
this.statsService.get().subscribe(s => this.stats = s);
}
ngOnDestroy(): void {
this.autoRefresh.unsubscribe(); // Stop polling when user leaves page
}
}
2.4 What is the difference between Constructor and Lifecycle Events?¶
constructor | ngOnInit | |
|---|---|---|
| When | Class is instantiated by DI | After Angular sets @Input bindings |
| Purpose | Inject dependencies | Business logic, API calls, form setup |
@Input available? | No | Yes |
| DOM available? | No | No (use ngAfterViewInit) |
export class UserComponent implements OnInit {
@Input() userId!: number;
// Only inject services here
constructor(private userService: UserService) {}
// Use @Input() values and run business logic here
ngOnInit(): void {
this.userService.getById(this.userId).subscribe(u => this.user = u);
}
}
Never make API calls in the constructor —
@Inputvalues haven't been set yet.
3. Data Binding¶
3.1 What is One-Way Data Binding?¶
Theory: Data flows in one direction — from component class to template, or template to component via events.
Interpolation {{ }}¶
Displays a component property as text:
export class AppComponent {
username = 'Alice';
today = new Date();
}
<p>Hello, {{ username }}!</p>
<p>Today: {{ today | date }}</p>
Interpolation always produces a string. For non-string DOM properties use property binding.
Property Binding [ ]¶
Binds a component property to a DOM property or component @Input:
<input [value]="username" [placeholder]="'Enter ' + username">
<button [disabled]="isLoading">Save</button>
<div [class.active]="isActive"></div>
<img [src]="imageUrl" [alt]="imageAlt">
<!-- Component @Input binding -->
<app-user-card [user]="currentUser" [showAvatar]="true"></app-user-card>
Event Binding ( )¶
Listens to DOM events and calls component methods:
<button (click)="saveUser()">Save</button>
<input (keyup)="onSearch($event)" (blur)="validate()">
<form (ngSubmit)="onSubmit()">...</form>
onSearch(event: Event): void {
const term = (event.target as HTMLInputElement).value;
this.filter(term);
}
3.2 What is Two-Way Data Binding?¶
Theory: Data flows both ways — component and template stay in sync automatically.
Method 1 — Template-Driven Forms [(ngModel)]¶
Requires FormsModule:
@Component({
standalone: true,
imports: [FormsModule],
template: `
<input [(ngModel)]="firstName" placeholder="First name">
<p>Hello, {{ firstName }}!</p>
`
})
export class FormComponent {
firstName = '';
}
[(ngModel)]is called "banana in a box" —[]binds the property,()emits changes.
Method 2 — Reactive Forms formControlName¶
Requires ReactiveFormsModule:
@Component({
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()">
<input formControlName="firstName">
<small *ngIf="form.get('firstName')?.invalid">Required</small>
<button type="submit">Submit</button>
</form>
`
})
export class ReactiveFormComponent implements OnInit {
form!: FormGroup;
ngOnInit() {
this.form = new FormGroup({
firstName: new FormControl('', [Validators.required, Validators.minLength(2)])
});
}
submit() {
console.log(this.form.value);
}
}
[(ngModel)] | formControlName | |
|---|---|---|
| Module | FormsModule | ReactiveFormsModule |
| Type | Template-driven | Code-driven |
| Validation | In HTML | In TypeScript |
| Best for | Simple forms | Complex, dynamic forms |
Method 3 — Custom [()] on Components¶
// counter.component.ts
@Component({
selector: 'app-counter',
template: `<button (click)="increment()">{{ value }}</button>`
})
export class CounterComponent {
@Input() value = 0;
@Output() valueChange = new EventEmitter<number>(); // must end with "Change"
increment() {
this.value++;
this.valueChange.emit(this.value);
}
}
<!-- parent.html -->
<app-counter [(value)]="count"></app-counter>
<p>Count: {{ count }}</p>
4. Directives¶
Directives are classes that add behavior or modify the appearance of DOM elements. * Component Directives -> Every Angular component is a directive. * Structural (*ngIf, *ngFor) → Change the DOM layout by adding/removing elements. * Attribute (ngClass, ngStyle)→ Change the appearance or behavior of an existing element.
4.1 What are Structural Directives?¶
Theory: Structural directives change the DOM structure — they add, remove, or repeat elements. Prefixed with *.
@if (Angular 17+):
@if (isLoggedIn) {
<app-dashboard />
} @else {
<app-login />
}
@for (Angular 17+):
@for (user of users; track user.id) {
<app-user-card [user]="user" />
} @empty {
<p>No users found.</p>
}
@switch:
@switch (role) {
@case ('admin') { <app-admin-panel /> }
@case ('user') { <app-dashboard /> }
@default { <app-login /> }
}
Custom structural directive:
@Directive({ selector: '[appHasRole]', standalone: true })
export class HasRoleDirective {
constructor(
private template: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private auth: AuthService
) {}
@Input() set appHasRole(role: string) {
if (this.auth.hasRole(role)) {
this.viewContainer.createEmbeddedView(this.template);
} else {
this.viewContainer.clear();
}
}
}
<button *appHasRole="'admin'">Delete User</button>
4.2 What are Attribute Directives?¶
Theory: Attribute directives change the appearance or behavior of an existing element without modifying the DOM structure.
Built-in:
<div [ngClass]="{ 'active': isActive, 'disabled': !isEnabled }">...</div>
<p [ngStyle]="{ 'color': textColor, 'font-size': fontSize + 'px' }">...</p>
Custom attribute directive:
@Directive({
selector: '[appHighlight]',
standalone: true
})
export class HighlightDirective {
@Input() appHighlight = 'yellow';
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('mouseenter')
onMouseEnter() {
this.renderer.setStyle(this.el.nativeElement, 'background', this.appHighlight);
}
@HostListener('mouseleave')
onMouseLeave() {
this.renderer.removeStyle(this.el.nativeElement, 'background');
}
}
<p appHighlight="lightblue">Hover over me!</p>
Real Use Case: Permission-based visibility, auto-focus, tooltip on hover, input masking.
5. Control Flow Statements¶
Theory: Angular 17+ introduced built-in control flow syntax as a faster, import-free replacement for *ngIf, *ngFor, *ngSwitch.
<!-- Conditional rendering -->
@if (user.isAdmin) {
<span class="badge bg-danger">Admin</span>
} @else if (user.isPremium) {
<span class="badge bg-warning">Premium</span>
} @else {
<span class="badge bg-secondary">User</span>
}
<!-- List rendering with empty state -->
@for (product of products; track product.id) {
<app-product-card [product]="product" />
} @empty {
<p class="text-muted">No products available.</p>
}
<!-- Deferred / lazy rendering -->
@defer (on viewport) {
<app-heavy-chart [data]="chartData" />
} @placeholder {
<div class="skeleton-chart"></div>
} @loading (minimum 500ms) {
<app-spinner />
}
trackin@foris required. Use a unique identifier likeitem.idfor optimal DOM reuse.
6. Signals¶
6.1 What is a Signal?¶
Theory: A Signal is a reactive primitive (Angular 16+) that holds a value and automatically notifies Angular when it changes — without relying on zone.js.
Signal value changes → Angular is notified → Only affected UI parts re-render
vs. Regular variables:
// Regular variable — zone.js must catch the change
name = 'Alice';
changeName() { this.name = 'Bob'; }
// Signal — Angular always knows when it changes
name = signal('Alice');
changeName() { this.name.set('Bob'); }
6.2 How do you create a Signal?¶
import { signal, computed, effect } from '@angular/core';
export class CounterComponent {
// Writable signal
count = signal(0);
// Signal with explicit type
user = signal<User | null>(null);
// Computed signal (derived, read-only)
doubleCount = computed(() => this.count() * 2);
isLoggedIn = computed(() => this.user() !== null);
}
6.3 How do you access a Signal?¶
Call the signal as a function:
// In TypeScript
console.log(this.count()); // 0
console.log(this.user()?.name); // 'Alice'
<!-- In template -->
<p>Count: {{ count() }}</p>
<p>Double: {{ doubleCount() }}</p>
@if (user()) {
<p>Welcome, {{ user()!.name }}</p>
}
6.4 How do you update a Signal using set and update?¶
count = signal(0);
// set() — replaces the value entirely
this.count.set(10);
// update() — transforms based on current value
this.count.update(current => current + 1);
this.count.update(n => n * 2);
// Object signals — immutable update
user = signal<User>({ name: 'Alice', age: 25 });
this.user.update(u => ({ ...u, age: 26 }));
| Method | When to use |
|---|---|
set(value) | You have a completely new value |
update(fn) | New value depends on the current value |
6.5 How do Signals work with Change Detection?¶
Theory: Signals enable fine-grained change detection — Angular only re-renders components that actually read a changed signal.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<p>{{ count() }}</p>`
})
export class SignalComponent {
count = signal(0);
constructor() {
// effect() runs side effects whenever tracked signals change
effect(() => {
console.log('Count changed:', this.count());
localStorage.setItem('count', String(this.count()));
});
}
}
Zoneless Angular (future):
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection()]
});
7. Linked Signal¶
What is a Linked Signal and how do you create one?¶
Theory: A linked signal (Angular 17.1+) is a writable signal whose value automatically recalculates when its dependencies change, but can also be manually overridden. It is a hybrid of signal() and computed().
import { signal, linkedSignal } from '@angular/core';
selectedCategory = signal<string>('electronics');
// Resets to default item whenever selectedCategory changes
selectedItem = linkedSignal(() => {
const category = this.selectedCategory();
return this.getDefaultItem(category);
});
changeCategory(cat: string) {
this.selectedCategory.set(cat); // selectedItem auto-resets
}
overrideItem(item: Item) {
this.selectedItem.set(item); // manual override still works
}
Real Use Case — Pagination reset on filter change:
filter = signal('');
currentPage = linkedSignal(() => {
this.filter(); // track filter dependency
return 1; // reset to page 1 whenever filter changes
});
const count = signal(0); - Computed Signal – Derived read-only value. const doubleCount = computed(() => count() * 2); - Effect – Runs code when a signal changes. effect(() => console.log(count())); - Linked Signal is a writable signal that derives its value from another signal while allowing direct updates, combining the benefits of writable and computed signals. - computed() is used for read-only derived state and cannot be modified manually, whereas linkedSignal() creates a writable signal that stays in sync with a source signal but also allows local updates. 8. Routing¶
8.1 What is Routing?¶
Theory: Routing lets Angular apps navigate between views without full page reloads. The Router maps URL paths to components and renders them inside <router-outlet>.
URL changes → Router matches path → Component renders in <router-outlet>
8.2 How do you create Routes?¶
// app.routes.ts
export const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
// Eager-loaded
{ path: 'home', component: HomeComponent },
// Lazy-loaded (better performance)
{
path: 'dashboard',
loadComponent: () =>
import('./pages/dashboard/dashboard.component').then(m => m.DashboardComponent)
},
// Protected with guard
{
path: 'admin',
loadComponent: () =>
import('./pages/admin/admin.component').then(m => m.AdminComponent),
canActivate: [authGuard]
},
// Route parameter
{ path: 'users/:id', component: UserDetailComponent },
// Wildcard 404
{ path: '**', redirectTo: '/home' }
];
Register in app.config.ts:
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes)]
};
8.3 What is router-outlet?¶
Theory: <router-outlet> is a placeholder directive where the Router renders the matched component.
<!-- layout.component.html -->
<nav>...</nav>
<main>
<router-outlet></router-outlet>
</main>
<footer>...</footer>
Named outlets for side panels:
<router-outlet></router-outlet>
<router-outlet name="sidebar"></router-outlet>
8.4 How do you navigate programmatically?¶
export class LoginComponent {
private router = inject(Router);
private activatedRoute = inject(ActivatedRoute);
onLoginSuccess(): void {
// Absolute path string
this.router.navigateByUrl('/dashboard');
// Array of path segments
this.router.navigate(['/users', userId]);
// With query params
this.router.navigate(['/cases'], { queryParams: { status: 'open' } });
// Relative navigation
this.router.navigate(['../profile'], { relativeTo: this.activatedRoute });
}
}
8.5 What is routerLink?¶
Theory: routerLink is a directive that turns any element into a navigation link without a full page reload.
<a routerLink="/home">Home</a>
<!-- Dynamic path -->
<a [routerLink]="['/users', user.id]">View Profile</a>
<!-- Query params -->
<a [routerLink]="['/cases']" [queryParams]="{ status: 'open' }">Open Cases</a>
<!-- Active class -->
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<!-- Exact match -->
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>
Import
RouterLinkandRouterLinkActivein standalone components'importsarray.
9. Pipes¶
What are Pipes and how do you create a custom one?¶
Theory: Pipes transform data in templates using the | operator without modifying the underlying data.
Built-in Pipes:
<p>{{ today | date:'mediumDate' }}</p> <!-- Apr 29, 2026 -->
<p>{{ price | currency:'USD' }}</p> <!-- $1,234.56 -->
<p>{{ 0.75 | percent }}</p> <!-- 75% -->
<p>{{ name | uppercase }}</p> <!-- ALICE -->
<p>{{ longText | slice:0:100 }}...</p> <!-- first 100 chars -->
<p>{{ user | json }}</p> <!-- { "name": "Alice" } -->
<p>{{ items$ | async }}</p> <!-- unwraps Observable -->
Creating a Custom Pipe:
// pipes/truncate.pipe.ts
@Pipe({ name: 'truncate', standalone: true })
export class TruncatePipe implements PipeTransform {
transform(value: string, maxLength = 100, suffix = '...'): string {
if (!value) return '';
return value.length > maxLength
? value.substring(0, maxLength) + suffix
: value;
}
}
<p>{{ description | truncate:50 }}</p>
<p>{{ longTitle | truncate:20:'...' }}</p>
Real Use Case — Status label pipe:
@Pipe({ name: 'statusLabel', standalone: true })
export class StatusLabelPipe implements PipeTransform {
transform(status: string): string {
const labels: Record<string, string> = {
'in-progress': 'In Progress',
'review': 'Under Review',
'completed': 'Done',
'draft': 'Draft'
};
return labels[status] ?? status;
}
}
<span class="badge">{{ caseStatus | statusLabel }}</span>
10. Resource API¶
What is the Resource API, and what are refresh/reload and loader?¶
Theory: The Resource API (Angular 19+) is a built-in, signal-based way to handle async data fetching — replacing the common pattern of manual subscribe + loading flag + error handling.
import { resource, signal } from '@angular/core';
import { firstValueFrom } from 'rxjs';
export class UserListComponent {
private http = inject(HttpClient);
searchTerm = signal('');
usersResource = resource({
request: () => ({ search: this.searchTerm() }),
loader: ({ request }) =>
firstValueFrom(
this.http.get<User[]>(`/api/users?q=${request.search}`)
)
});
}
@if (usersResource.isLoading()) {
<app-spinner />
} @else if (usersResource.error()) {
<p class="error">{{ usersResource.error() }}</p>
} @else {
@for (user of usersResource.value(); track user.id) {
<app-user-card [user]="user" />
}
}
<button (click)="usersResource.reload()">Refresh</button>
| Signal / Method | Purpose |
|---|---|
.value() | The loaded data (undefined while loading) |
.isLoading() | true while request is in flight |
.error() | Error thrown by loader |
.reload() | Re-runs loader with same request |
.set(value) | Manually override the value |
Real Use Case: Auto-refresh a case list when a filter signal changes; skeleton loaders; retry-on-error buttons.
11. Component Communication¶
What is @Input / @Output and how do you create a reusable component?¶
Theory:
@Input— passes data parent to child@Output— sends events child to parent
Parent to Child — @Input and @Output:
// user-card.component.ts (child)
@Component({
selector: 'app-user-card',
template: `
<div class="card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<button (click)="onEdit()">Edit</button>
</div>
`
})
export class UserCardComponent {
@Input({ required: true }) user!: User;
@Input() showActions = true;
@Output() editClicked = new EventEmitter<User>();
onEdit() {
this.editClicked.emit(this.user);
}
}
<!-- parent.component.html -->
<app-user-card
[user]="currentUser"
[showActions]="isAdmin"
(editClicked)="onUserEdit($event)">
</app-user-card>
// parent.component.ts
onUserEdit(user: User): void {
this.router.navigate(['/users', user.id, 'edit']);
}
Modern signal-based inputs (Angular 17.1+):
export class UserCardComponent {
user = input.required<User>();
showActions = input(true);
editClicked = output<User>();
onEdit() {
this.editClicked.emit(this.user());
}
}
Reusable component checklist:
- Accept all data via
@Input— no direct service calls inside the component - Communicate up via
@Outputevents — don't navigate internally - Keep it presentational (dumb) — no business logic
- Provide sensible
@Inputdefaults - Use
OnPushchange detection for performance
12. Angular CLI — Global vs Local Install¶
Is it necessary to install Angular globally? When should you choose global vs local?¶
Short answer: No — a global install is not required. It depends on your workflow.
When global install helps¶
- You work on Angular projects frequently
- You want to run
ng new,ng servefrom anywhere withoutnpx - You prefer convenience over strict version control
npm install -g @angular/cli
ng new my-project # works from any directory
When to avoid global install¶
- Working on multiple projects with different Angular versions
- In team environments where version consistency matters
- In CI/CD pipelines where reproducibility is critical
# Install locally per project
npm install @angular/cli --save-dev
# Run using npx or npm scripts
npx ng serve
npm run start
Best Practice in Teams¶
Use local install + npm scripts so every developer and CI pipeline uses the same version:
{
"scripts": {
"start": "ng serve",
"build": "ng build"
},
"devDependencies": {
"@angular/cli": "^19.0.0"
}
}
npm run start # always uses the local Angular CLI version
13. RxJS¶
13.1 What is RxJS and why is it used in Angular?¶
Theory: RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using Observables — a composable way to handle async operations, events, and data streams.
| Angular Feature | How RxJS is used |
|---|---|
HttpClient | All methods return Observable |
ReactiveFormsModule | control.valueChanges is an Observable |
Router | router.events is an Observable |
ActivatedRoute | route.paramMap is an Observable |
| NgRx (State) | Built entirely on RxJS |
Why RxJS — composable async:
// Without RxJS — callback hell
getUser(id, (user) => {
getOrders(user.id, (orders) => {
getDetails(orders[0].id, (details) => { /* deeply nested */ });
});
});
// With RxJS — readable pipeline
this.userService.getUser(id).pipe(
switchMap(user => this.orderService.getOrders(user.id)),
switchMap(orders => this.orderService.getDetails(orders[0].id))
).subscribe(details => this.details = details);
13.2 What is an Observable?¶
Theory: An Observable is like a subscription — you subscribe once and keep receiving data over time. It can emit multiple values, error out, or complete.
import { Observable } from 'rxjs';
// Creating an Observable
const counter$ = new Observable<number>(subscriber => {
let count = 0;
const id = setInterval(() => {
subscriber.next(count++);
if (count > 5) {
subscriber.complete();
clearInterval(id);
}
}, 1000);
});
// Subscribing
const sub = counter$.subscribe({
next: value => console.log('Value:', value), // 0, 1, 2, 3, 4, 5
error: err => console.error('Error:', err),
complete: () => console.log('Done!')
});
// Always unsubscribe to prevent memory leaks
sub.unsubscribe();
Common RxJS operators:
this.searchControl.valueChanges.pipe(
debounceTime(300), // Wait for user to stop typing
filter(term => term.length > 2), // Skip short queries
switchMap(term => // Cancel previous, start new request
this.searchService.search(term)
),
catchError(err => { // Handle errors gracefully
this.error = err.message;
return of([]);
})
).subscribe(results => this.results = results);
13.3 What is the difference between a Promise and an RxJS Observable?¶
| Feature | Promise | Observable |
|---|---|---|
| Values | One value (resolved once) | Multiple values over time |
| Lazy? | No — executes immediately | Yes — only runs on .subscribe() |
| Cancelable? | No | Yes — .unsubscribe() |
| Operators | .then(), .catch() | 100+ operators (map, filter, switchMap, ...) |
| Best for | Single async result | Streams, events, retries |
// Promise — fires immediately
const promise = fetch('/api/user').then(r => r.json());
// Observable — lazy, cancelable
const user$ = this.http.get<User>('/api/user');
// Nothing happens until subscribe:
const sub = user$.subscribe(user => console.log(user));
sub.unsubscribe(); // can cancel mid-flight
13.4 Why does HttpClientProvider need to be configured at app level?¶
Theory: provideHttpClient() registers the HttpClient service and its interceptor pipeline in Angular's root injector. Because HttpClient is used across the entire app, providing it at the root level ensures:
- All components and services share one instance
- Interceptors run for every request across the entire app
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor])
)
]
};
If provided inside a component, each component instance gets its own
HttpClient— interceptors won't be shared.
14. Route Guards¶
14.1 What are the Types of Route Guards?¶
Theory: Route guards control navigation — they decide whether a user can enter or leave a route.
| Guard | Type | Purpose |
|---|---|---|
canActivate | CanActivateFn | Can user access this route? |
canActivateChild | CanActivateChildFn | Can user access child routes? |
canDeactivate | CanDeactivateFn | Can user leave this route? (e.g., unsaved changes) |
canMatch | CanMatchFn | Should this route definition be matched at all? |
resolve | ResolveFn | Pre-fetch data before the route activates |
// auth.guard.ts
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn()
? true
: router.createUrlTree(['/login'], { queryParams: { returnUrl: state.url } });
};
// unsaved-changes.guard.ts
export const unsavedChangesGuard: CanDeactivateFn<EditFormComponent> = (component) => {
if (component.hasUnsavedChanges()) {
return confirm('You have unsaved changes. Leave anyway?');
}
return true;
};
// Wiring in routes
export const routes: Routes = [
{
path: 'profile/edit',
component: EditProfileComponent,
canActivate: [authGuard],
canDeactivate: [unsavedChangesGuard]
}
];
14.2 Why write Function-based Guards instead of Class-based Guards?¶
Class-based (old way):
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(): boolean | UrlTree {
return this.auth.isLoggedIn()
? true
: this.router.createUrlTree(['/login']);
}
}
Function-based (recommended — Angular 15+):
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn() ? true : router.createUrlTree(['/login']);
};
Why functions are preferred:
| Reason | Explanation |
|---|---|
| Less boilerplate | No class, no @Injectable(), no constructor |
inject() instead of constructor DI | Works cleanly inside functions |
| Better tree-shaking | Build tools can optimize unused code more easily |
| Angular direction | Angular is standardizing on functional APIs |
14.3 When should you use Class-based Guards instead of Functions?¶
1. Shared logic across multiple guards:
export abstract class BaseAuthGuard {
protected auth = inject(AuthService);
protected router = inject(Router);
protected redirectToLogin(): UrlTree {
return this.router.createUrlTree(['/login']);
}
}
@Injectable({ providedIn: 'root' })
export class AdminGuard extends BaseAuthGuard implements CanActivate {
canActivate(): boolean | UrlTree {
return this.auth.isAdmin() ? true : this.redirectToLogin();
}
}
2. Guard needs to hold state:
@Injectable({ providedIn: 'root' })
export class NavigationHistoryGuard implements CanActivate {
private history: string[] = []; // stateful
canActivate(route: ActivatedRouteSnapshot): boolean {
this.history.push(route.url.join('/'));
return true;
}
getHistory(): string[] { return this.history; }
}
3. Complex multi-service dependencies:
@Injectable({ providedIn: 'root' })
export class FeatureFlagGuard implements CanActivate {
constructor(
private flags: FeatureFlagService,
private config: AppConfigService,
private analytics: AnalyticsService
) {}
canActivate(route: ActivatedRouteSnapshot): boolean {
const feature = route.data['feature'];
this.analytics.track('route-guard-check', feature);
return this.flags.isEnabled(feature) && this.config.isAllowed(feature);
}
}
Decision Guide:
Need shared logic across guards? → Class
Need to store state inside the guard? → Class
Complex multi-service dependency? → Class
Simple auth check? → Function
Single service check? → Function
New project (Angular 15+)? → Function (default)
15. JWT Security Best Practices¶
What are the best practices for storing JWT tokens in Angular? How do you hide them from users?¶
Storage options compared:
| Storage | XSS Risk | CSRF Risk | Accessible to JS | Recommendation |
|---|---|---|---|---|
localStorage | High | None | Yes | Avoid for tokens |
sessionStorage | High | None | Yes | Short sessions only |
| Cookie (no flags) | High | High | Yes | Never |
HttpOnly Cookie | None | Medium | No | Best for refresh token |
| Memory (service signal) | Low | None | Scoped to tab | Best for access token |
Recommended Pattern — Store access token in memory only:
// auth.service.ts
@Injectable({ providedIn: 'root' })
export class AuthService {
// Private signal — not persisted, not accessible from outside
private accessToken = signal<string | null>(null);
private userRole = signal<string | null>(null);
setTokens(accessToken: string, role: string): void {
this.accessToken.set(accessToken);
this.userRole.set(role);
// Never do: localStorage.setItem('token', accessToken)
}
getAccessToken(): string | null { return this.accessToken(); }
isLoggedIn = computed(() => this.accessToken() !== null);
role = computed(() => this.userRole());
logout(): void {
this.accessToken.set(null);
this.userRole.set(null);
}
}
// auth.interceptor.ts — attaches token from memory to every request
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
const token = auth.getAccessToken();
if (token) {
req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}
return next(req);
};
Additional security rules:
- Use short-lived access tokens (15 min) with long-lived refresh tokens (7 days, stored in HttpOnly cookie by backend)
- Never log tokens in the console or error tracking tools
- Use HTTPS only in production
- Implement token rotation — issue a new refresh token on each use
- Always validate tokens server-side — never trust the client
Last updated: May 2026