Learning Partner

Angular Questions

Signals, RxJS, NgRx, Ivy, micro-frontends, and change detection: ace the questions senior Angular roles actually ask.

Change Detection

Change Detection is the mechanism Angular uses to update the DOM when the application state changes. Because with Default Change Detection, Angular runs change detection from the root component down the entire tree, regardless of whether a component's @Input()s changed or not. So even if the @Input() values passed to the child remain the same (or don’t exist at all), the child will still go through change detection, and Angular will re-evaluate its template expressions to see if anything needs updating. It doesn’t care why something may have changed. Angular just assumes something might have changed: so it runs CD on everything.

  • Angular executes an event (e.g., click, setTimeout).
  • Zone.js detects it.
  • Angular runs Change Detection starting from the root component and traverses the component tree.
  • It compares the current and previous values of bindings and updates the DOM if needed.
constructor(private changeDetectionRef:ChangeDetectorRef) { }
this.changeDetectionRef.detectChanges();
constructor(private changeDetectionRef:ChangeDetectorRef) { }
this.changeDetectionRef.detectChanges();

NgZone

NgZone is Angular's service that provides a controlled execution context for running code inside or outside Angular's zone.

  • Purpose: Executes the given function inside the Angular zone, ensuring that Angular's change detection is triggered after the function's execution.
  • Use Case: When you've executed some code outside Angular's zone (e.g., for performance reasons) and need to re-enter the Angular zone to ensure the UI updates correctly.
this.ngZone.run(() => {
  // Code that needs to run inside Angular's zone
});
this.ngZone.runOutsideAngular(() => {
  // Code that runs outside Angular's zone
});
this.ngZone.runOutsideAngular(() => {
  // Perform some background task
  this.ngZone.run(() => {
    // Update UI components here
  });
});

Hierarchical Dependency Injection

Angular has a built-in DI framework that manages how services or objects are instantiated and how they get injected into components, directives, pipes, or other services. This is done using metadata like the @Injectable() decorator and constructor injection. If we don't add the service in any component provider array we have single instance of that service across all components, so If I am storing some data from component that data i can read in another component now for NgContanierTempComponent, we have separate instance of ProductService service so data store from ChnageDetectionComponent component won't be present in service object as we have different objcet available for this component if we have another child component rendered in this component it will share same instance of service we have over here not global one

export class ProductService {
  data: any = {};
}
export class ChnageDetectionComponent {
  constructor(private prod: ProductService) {}
  addVale() {
    this.prod.data.name = 'Angular';
  }
}
export class NgContanierTempComponent {
  constructor(private prod: ProductService) {}
  read() {
    const serviceData = this.prod.data;
  }
}

create a mock instance of a service

To create a mock instance of a service, you typically want to simulate a service's behavior without actually calling its real implementation: especially useful in unit testing. We create a mock class with matching method signatures that returns test doubles (e.g. of(['mock', 'data'])) without invoking real backend endpoints.

class MockMyService {
  getData() {
    return of(['mock', 'data']);
  }
}
TestBed.configureTestingModule({
  providers: [{ provide: MyService, useClass: MockMyService }],
});

multiple ways to configure providers

Detailed explanation and practical code examples below.

providers: [{ provide: LoggService, useClass: ConsoleLogService }];
providers: [{ provide: NewService, useExisting: ExistingService }];
providers: [{ provide: API_ENDPOINT, useValue: 'https://api.exa.com' }];
providers: [
  {
    provide: ConfigService,
    useFactory: (env: EnvironmentService) => new ConfigService(env.isProd),
    deps: [EnvironmentService],
  },
];

Provider Scopes (providedIn options)

providedIn: 'root' (Default) Scope: Application-wide singleton. A single instance is shared across the entire application. No need to add the service to any module's providers array Supports tree-shaking; unused services are excluded from the final bundle. providedIn: 'any' Each lazy-loaded module receives its own instance; eagerly loaded modules share a singleton. Eagerly loaded modules share a single instance.​ Each lazy-loaded module gets a new instance.​ Useful when you want to isolate service instances between lazy-loaded modules to prevent shared state.​ providedIn: 'platform' Singleton shared across all Angular applications on the same page.​ he service is provided in the platform injector, making it available to multiple Angular apps running simultaneously Beneficial in micro-frontend architectures or when using Angular Elements, where multiple Angular applications coexist. providedIn: SomeModule Specific to the module where it's provided. The service is available only within the specified module When you want to restrict a service's availability to a particular feature module.

  • Scope: Application-wide singleton.
  • A single instance is shared across the entire application.
  • No need to add the service to any module's providers array
  • Supports tree-shaking; unused services are excluded from the final bundle.
  • Each lazy-loaded module receives its own instance; eagerly loaded modules share a singleton.
  • Eagerly loaded modules share a single instance.​
  • Each lazy-loaded module gets a new instance.​
  • Useful when you want to isolate service instances between lazy-loaded modules to prevent shared state.​
  • Singleton shared across all Angular applications on the same page.​
  • he service is provided in the platform injector, making it available to multiple Angular apps running simultaneously
  • Beneficial in micro-frontend architectures or when using Angular Elements, where multiple Angular applications coexist.
  • Specific to the module where it's provided.
  • The service is available only within the specified module
  • When you want to restrict a service's availability to a particular feature module.
@Injectable({ providedIn: 'root' })
export class GlobalService {}
@Injectable({ providedIn: 'any' })
export class ModuleScopedService {}
@Injectable({ providedIn: 'platform' })
export class SharedPlatformService {}

service Decorators

@Self(): Only look for the dependency in the current injector.​ @SkipSelf(): Look for the dependency in parent injectors.​ @Optional(): If the dependency isn't found, inject null instead of throwing an error.​ @Host(): Look for the dependency in the current component's injector or its host.​

  • @Self(): Only look for the dependency in the current injector.​
  • @SkipSelf(): Look for the dependency in parent injectors.​
  • @Optional(): If the dependency isn't found, inject null instead of throwing an error.​
  • @Host(): Look for the dependency in the current component's injector or its host.​

Angular Ivy

Just like how we use console in JavaScript same way we can debug angular app as well Each component is compiled in isolation. No need for the full app context. Unused parts of Angular and components are dropped during build.

  • Ivy is the new rendering engine introduced in Angular 9
  • Think of Ivy as the engine that powers how Angular compiles and renders your components
  • Ivy exposes internal APIs like ng.getComponent(), ng.getInjector(), and ng.getHostElement() in the browser console.
  • Enables faster builds and rebuilds
  • Makes library development modular
  • Ivy's compiler generates code that's more tree-shakable.
  • Smaller bundles = better performance.
  • Templates are compiled to highly optimized JS instructions (ɵɵ instructions).
  • This removes the need for metadata JSON files (.metadata.json) and factory files (.ngfactory.ts), making libraries easier to publish and consume.
const cmp = ng.getComponent(document.querySelector('my-component'));
console.log(cmp.someProperty);

How Ivy Works

your template Angular templates are compiled into instructions like:

ɵɵelementStart(0, 'div');
ɵɵtext(1, 'Hello Ivy');
ɵɵelementEnd();

Implement Micro-Frontend using Module Federation

Go to Shell Project and run below Go to Remote Project and run below open remote/webpack.config.js shell/webpack.config.js app.config of shell run remote Project run shell Project

ng new shell --routing --style=scss ng new remote --routing --style=scss
ng add @angular-architects/module-federation --project shell --type host Enter
port 4200 Select Module Federation with webpack (classic) Select Y
ng add @angular-architects/module-federation --project remote --type remote
Enter port 4201 Select Module Federation with webpack (classic) Select Y
const {
  shareAll,
  withModuleFederationPlugin,
} = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  name: 'remote',
  exposes: {
    './Component': './src/app/app.component.ts',
  },
  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: 'auto',
    }),
  },
});
const {
  shareAll,
  withModuleFederationPlugin,
} = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  remotes: {
    remote: 'remote@http://localhost:4201/remoteEntry.js',
  },
  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: 'auto',
    }),
  },
});
const routes: Route[] = [
  {
    path: 'remote',
    loadComponent: () =>
      loadRemoteModule({
        type: 'module',
        remoteEntry: 'http://localhost:4201/remoteEntry.js',
        exposedModule: './Component',
      }).then((m) => m.AppComponent),
  },
];

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
  ],
};
ng serve remote
ng serve shell

ng-container

<ng-container> is a special grouping element that Angular does not render into the DOM tree. It acts as an invisible host for structural directives without adding unnecessary wrapper elements (like <div> or <span>).

  • Prevents DOM bloat by grouping elements without rendering extra tags.
  • Allows applying structural directives (*ngIf, *ngFor) without breaking HTML layout (e.g. <tr>/<td> in tables).
  • Acts as a target container for dynamic view insertion using ViewContainerRef.
<ng-container *ngIf="isLoggedIn">
  <h2>Welcome Back!</h2>
  <p>Here is your personalized dashboard.</p>
</ng-container>

ngTemplateOutlet

ngTemplateOutlet dynamically inserts a template defined by <ng-template> into the DOM. It also allows passing custom context data into the template for reusable UI components.

  • Renders an <ng-template> dynamically inside an <ng-container>.
  • Supports passing custom context objects via [ngTemplateOutletContext].
  • Ideal for customizable cards, reusable table columns, and dynamic widgets.
<ng-template #userBadge let-name="name" let-role="role">
  <div class="badge"><strong>{{ name }}</strong> - <span>{{ role }}</span></div>
</ng-template>

<ng-container
  *ngTemplateOutlet="userBadge; context: { name: 'Alice', role: 'Admin' }"
></ng-container>
<ng-container
  *ngTemplateOutlet="userBadge; context: { name: 'Bob', role: 'User' }"
></ng-container>

Conditional Templates with ngIf and else

Using <ng-template> in conjunction with *ngIf; else allows clean view swapping for loading states, error fallbacks, or conditional views without nested checks.

  • Provides a declarative fallback (else) block for *ngIf.
  • Keeps template logic clean without writing duplicate opposite if conditions.
<div *ngIf="user$ | async as user; else loadingTemplate">
  <h3>{{ user.name }}</h3>
  <p>{{ user.email }}</p>
</div>

<ng-template #loadingTemplate>
  <div class="spinner">Loading user profile...</div>
</ng-template>

Dynamic component into ng-container

Detailed explanation and practical code examples below.

@Component({
  selector: 'app-view-child-content',
  imports: [RxjsOperatorComponent],
  templateUrl: './view-child-content.component.html',
  styleUrl: './view-child-content.component.css',
})
export class ViewChildContentComponent implements AfterViewInit {
  @ViewChild('dTem', { read: ViewContainerRef }) container!: ViewContainerRef;
  constructor(private viewContainerRef: ViewContainerRef) {}
  ngAfterViewInit(): void {
    this.container.createComponent(MyButtonComponent);
  }
}

Step 1: Create Actions

Actions define what can happen in the application. They are simple objects describing an event. actions don't contain any logic; they just describe what happens means when you create your store what Operation you can provide with that store that you define over here. Like for Counter Increment and Decrement counter.actions.ts When the user clicks the "Increment" button, we dispatch the increment action.

import { createAction } from '@ngrx/store';

export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
export const reset = createAction('[Counter] Reset');

Step 2:Create Reducer

A reducer is a pure function that takes the current state and an action and returns the new state. Reducers are pure functions, meaning they don't modify the originnal state but return a new state. counter.actions.ts createReducer() defines how the state should change based on actions:

import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';

export const initialState = 0;

export const counterReducer = createReducer(
  initialState,
  on(increment, (state) => state + 1),
  on(decrement, (state) => state - 1),
  on(reset, () => 0),
);

Step 3: Create State Interface

app.state.ts

export interface AppState {
  count: number;
}

Step 4: Register Reducer in Store in app.module.ts

The Store is like a global state container that holds all application state app.module.ts app.config.ts StoreModule.forRoot({ count: counterReducer }) => Registers the counterReducer in the global store. and count becomes the key in the state.

import { counterReducer } from './store/counter/counter.reducer';
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ count: counterReducer }), // Register reducer
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideStore({ count: counterReducer }),
    provideRouter(routes),
  ],
};
{ "count": 0 }

Step 5: How to Acces store and Update store from component

The Store is like a global state container that holds all application state app.component.ts ,| => angular bracket

count$: Observable |number|; constructor(private store: Store|AppState|) {
this.count$ = this.store.pipe(select('count')); } increment() {
this.store.dispatch(increment()); } decrement() {
this.store.dispatch(decrement()); } reset() { this.store.dispatch(reset()); }

How Above works

The Store is like a global state container that holds all application state store.select('count') => this is how we read data from store by select method and prviding state name The reason we use this.store.pipe(select('count')) instead of this.store.select('count') directly is due to how RxJS and NgRx handle state selection. select() is an RxJS operator and In NgRx, select() is not a method on Store , Since it is an operator, it must be used inside dispatch action => this actually by using action we are trigging our reducer

[ User Clicks Button ] ↓ [ Component Dispatches Action ] ↓ [ Action Sent to
Store ] ↓ [ Reducer Updates State ] ↓ [ Store Emits New State ] ↓ [ Component
Receives Updated State ] ↓ [ UI Updates Automatically ]

How to Optimize Angular Applications

Use Lazy Loading (Route-Level Code Splitting) Split app into modules (AdminModule, UserModule, etc.) Use Angular Router’s loadChildren to lazy load those modules Enable Change Detection Optimization Only re-renders component if @Input() changes Improves performance, especially in large UIs Use TrackBy in *ngFor With trackBy, only changed items are updated Avoid Memory Leaks (Especially in RxJS) Always unsubscribe from subscriptions using: takeUntil(), AsyncPipe Subscription management in ngOnDestroy Use Pure Pipes for Expensive Transformations Pure pipes are only re-evaluated when input changes. Use Web Workers for Heavy Computation Offload CPU-heavy tasks (e.g., data parsing, image processing) to Web Workers. Lazy Load Images and Components Use loading="lazy" on

  • Split app into modules (AdminModule, UserModule, etc.)
  • Use Angular Router’s loadChildren to lazy load those modules
  • Only re-renders component if @Input() changes
  • Improves performance, especially in large UIs
  • With trackBy, only changed items are updated
  • Always unsubscribe from subscriptions using:
  • takeUntil(), AsyncPipe
  • Subscription management in ngOnDestroy
  • Pure pipes are only re-evaluated when input changes.
  • move Offload CPU-heavy tasks (e.g., data parsing, image processing) to Web Workers.
  • Use loading="lazy" on

How do you secure an Angular app?

Route Guards We should use canActivate, canLoad, and canActivateChild to restrict unauthorized access, based on roles or auth status. Interceptors use HttpInterceptors to inject tokens into headers securely, and also handle unauthorized responses globally. CSRF Protection For cookie-based auth, I work with backend teams to enable CSRF protection using X-XSRF-TOKEN, which Angular supports out of the box. Avoid Exposing Secrets make sure not to hardcode API keys or secrets in Angular, since all front-end code is publicly visible. Token Security we should prefer storing JWTs in HttpOnly cookies to avoid XSS risks. If localStorage or sessionStorage is used, we should ensure input sanitization and logout on suspicious activity. HttpOnly cookies: They are stored by the browser itself, and not accessible via document.cookie or JavaScript. You only see them in the "Application → Cookies" tab in dev tools. this.http.get('/api/user-profile', { withCredentials: true }).subscribe(...)

  • We should use canActivate, canLoad, and canActivateChild to restrict unauthorized access, based on roles or auth status.
  • use HttpInterceptors to inject tokens into headers securely, and also handle unauthorized responses globally.
  • For cookie-based auth, I work with backend teams to enable CSRF protection using X-XSRF-TOKEN, which Angular supports out of the box.
  • make sure not to hardcode API keys or secrets in Angular, since all front-end code is publicly visible.
  • we should prefer storing JWTs in HttpOnly cookies to avoid XSS risks.
  • If localStorage or sessionStorage is used, we should ensure input sanitization and logout on suspicious activity.
  • HttpOnly cookies: They are stored by the browser itself, and not accessible via document.cookie or JavaScript. You only see them in the "Application → Cookies" tab in dev tools.
  • this.http.get('/api/user-profile', { withCredentials: true }).subscribe(...)
imports: [
  HttpClientXsrfModule.withOptions({
    cookieName: 'MY-XSRF-COOKIE',
    headerName: 'MY-XSRF-HEADER',
  }),
];

Code-Level Optimizations

Split large modules into lazy-loaded chunks using routes: Angular and Webpack remove unused code ( but only if it's structured well) Helps reduce boilerplate, improves tree-shakability and bundling.

  • Avoid large barrel exports
  • Don’t bundle everything in a single shared module
  • Mark classes with providedIn: 'root' for optimal tree-shaking
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m =>
m.AdminModule) }

Build-Time Optimizations

When you write Angular code using TypeScript, SCSS, etc., it's not what the browser actually runs. The browser gets compiled JavaScript, CSS, and HTML. But when you're debugging, you want to see your original TypeScript code, not the ugly minified JS. That’s where source maps come in, they’re like a translator between your original source code and the compiled output.

  • Faster rendering
  • Smaller bundles
  • Detects template errors at build time
  • The Angular CLI now supports ESBuild: much faster than Webpack for transpiling.
  • create separate bundles for modern and legacy browsers.
  • Angular CLI handles this automatically es2015 for modern browsers es5 fallback for older ones
  • es2015 for modern browsers
  • es5 fallback for older ones
"configurations": { "production": { ... "sourceMap": false } }

Runtime Optimization Techniques

Use ChangeDetectionStrategy.OnPush to avoid unnecessary checks: Memoization is an optimization technique where you cache the result of expensive function calls and return the cached result when the same inputs occur again.

  • Use WebP or AVIF
  • Lazy load heavy images
trackById(index: number, item: Item) {
  return item.id;
}
}

What is Webpack

Webpack is a module bundler. Think of it as a super-efficient packer: it takes all your project files (JavaScript, CSS, images, HTML, etc.), analyzes their dependencies, and bundles them into optimized files that can be shipped to the browser. Webpack is the engine behind Angular CLI's build system. It bundles your code, optimizes it, splits it for lazy loading, processes your assets, and prepares everything for efficient browser delivery.

  • Webpack takes your Angular components, services, pipes, directives, and other modules and builds a dependency graph.
  • Then it bundles these modules into efficient JavaScript files (like main.js, polyfills.js, etc.).
  • Webpack loads and processes your styles (CSS/SCSS), HTML templates, and images using loaders
  • Removes unused code (dead code elimination).
  • This significantly reduces bundle size in production builds
  • Webpack enables lazy loading and dynamic imports to split your app into smaller chunks.
  • This means only the necessary code is loaded initially, improving performance.
  • Angular uses Webpack to replace environment files (environment.ts vs environment.prod.ts) during build time.
  • Removes white spaces, comments, shortens variable names, etc.
  • Also optimizes images, CSS, and other static files
  • Generates .map files so that even after code minification, you can debug using original source code in the browser.

What is Babel?

Babel is a JavaScript compiler. Think of it like a translator that:

  • Converts modern JavaScript (ES6+ or TypeScript) into older versions (like ES5) that all browsers can understand.
  • Allows use of cutting-edge JS features without worrying about browser support.
  • Transpile ES6+ code to ES5
  • Transpile JSX (React syntax) to plain JS
  • Polyfill new APIs like Promise, Array.from, etc.

XSS (Cross-Site Scripting)

XSS is when an attacker injects malicious JavaScript into your application: often via input fields, query params, or URLs.

p  {{ userInput }} p
 userInput ="<script>alert('x')script>"
[innerHTML] = 'userInput';
this.sanitizer.bypassSecurityTrustHtml(userInput);

AOT vs JIT

Angular compiles your HTML templates + TypeScript code in the browser, as the app loads. Angular pre-compiles your templates and code before shipping to the browser.

  • You write TS and templates.
  • Angular sends them raw-ish to the browser.
  • Angular compiler runs in the browser and compiles templates into JS on the fly.
  • Angular CLI compiles HTML & TS into pure JavaScript during ng build.
  • Removes Angular compiler from final bundle.
  • Faster rendering in the browser, Smaller bundle size,Early error detection

Accessibility in angular

We ensure all interactive elements are keyboard-accessible using tabindex, role, and HostListener for key events." use LiveAnnouncer and FocusTrap from Angular CDK to make dynamic interactions accessible. avoid using divs or spans for interactivity unless I add the necessary ARIA roles and keyboard logic.

  • Keyboard Navigation: Users should be able to use the app using just a keyboard (Tab, Enter, Space, etc.).
  • Screen Readers: The UI should be understandable by screen readers (like NVDA, JAWS).
  • Semantic HTML: Use elements for what they mean (, , , etc.).
  • Color Contrast: Make sure there's enough contrast between text and background.
  • Focus Management: Properly manage focus (especially after navigation/dialogs).
  • ARIA attributes: aria-label, aria-hidden, role, etc., to enhance semantics for assistive tech.
  • Live announcements with cdk/LiveAnnouncer

switchMap Operator

switchMap projects each source value to an inner Observable. When a new source value arrives, switchMap immediately cancels (unsubscribes from) any pending inner Observable and switches to the new one.

  • Cancels in-flight inner HTTP requests when a newer event occurs.
  • Perfect for typeahead/search autocomplete where only the latest keystroke response matters.
  • Prevents race conditions where a slow previous response overwrites a faster recent response.
  • Do not use for save/submit operations where every request must complete (use mergeMap or concatMap instead).
searchControl.valueChanges
  .pipe(
    debounceTime(300),
    distinctUntilChanged(),
    switchMap((query) => this.http.get('/api/search?q=' + query)),
  )
  .subscribe((results) => {
    this.searchResults = results;
  });

@ViewChild

Accesses elements, directives, or components within the view DOM of the current component. When you need to interact with or manipulate child components or elements that are part of the component's own template we can create instance of Html Element or Component instance in template in component.ts in template in .ts

  • Accesses elements, directives, or components within the view DOM of the current component.
  • When you need to interact with or manipulate child components or elements that are part of the component's own template
  • we can create instance of Html Element or Component instance
input type="text" #txt
@ViewChild('txt') txtInput!: ElementRef;
readValue() {
  const value = this.txtInput.nativeElement.value;
}
}
app - rxjs - operator - app - rxjs - operator;
@ViewChild(RxjsOperatorComponent) rxjsComp!: RxjsOperatorComponent;
readValue() {
  const nameFromChildPage = this.rxjsComp.firstName;
}
}

@ContentChild

Accesses elements or components that are projected into the component using ng-content directive When you need to interact with or manipulate content projected from a parent component into the child component child component Templae in app-component template

  • Accesses elements or components that are projected into the component using ng-content directive
  • When you need to interact with or manipulate content projected from a parent component into the child component
@Component({ selector: 'app-my-button', imports: [], template: 'button
class="btn btn-success"> ng-content-ng-content> button', styleUrl:
'./my-button.component.css' }) export class MyButtonComponent {
@ContentChild('icon') iconEle! : ElementRef; constructor() { setTimeout(() => {
debugger; const ss = this.iconEle.nativeElement.innerText; }, 6000); } }
app-my-button span #icon class="fa fa-user" span span Save User span
app-my-button

ViewContainerRef

Represents a container where one or more views can be dynamically added or removed means instead of usi9ng selector to render component we create instance of component in.ts and inject that into ng-container Template .ts code

  • Represents a container where one or more views can be dynamically added or removed
  • means instead of usi9ng selector to render component we create instance of component in.ts and inject that into ng-container
@ViewChild('dyanamicTem', { read: ViewContainerRef })
container!: ViewContainerRef;
ngAfterViewInit(): void {
  this.container.createComponent(MyButtonComponent);
}
}

What is a Web Worker

A Web Worker runs JavaScript in a background thread, separate from the main UI thread. So if something is CPU-intensive (like parsing, encrypting, image processing, etc.), you can offload it to a worker and keep the UI fast and responsive. You're building a finance dashboard (think high-frequency trading platform) in Angular, and you're fetching gigantic JSON datasets every 5 seconds. Parsing them in the main thread causes frame drops and freezes the UI You're building an Angular reporting tool where users can download detailed reports (with tables, charts, and summaries) as PDFs. But generating a PDF (especially with large data and canvas charts) takes 3–5 seconds and blocks the UI: your app freezes.

ng generate web-worker dataParser
addEventListener('message', ({ data }) => {
  const parsedData = JSON.parse(data);
  const processed = parsedData.map((entry: any) => ({
    ...entry,
    amountUSD: entry.amount * 80,
  }));
  postMessage(processed);
});
const worker = new Worker(new URL('./data-parser.worker', import.meta.url), {
  type: 'module',
});
worker.onmessage = ({ data }) => {
  this.processedData = data;
};
this.http.get('url', { responseType: 'text' }).subscribe((rawJson) => {
  worker.postMessage(rawJson);
});
import * as pdfMake from 'pdfmake/build/pdfmake';
import * as pdfFonts from 'pdfmake/build/vfs_fonts';
(pdfMake as any).vfs = pdfFonts.pdfMake.vfs;

addEventListener('message', ({ data }) => {
  const { tableData, title } = data;

  const docDef = {
    content: [
      { text: title, style: 'header' },
      {
        table: {
          body: [
            ['Name', 'Amount', 'Date'],
            ...tableData.map((row: any) => [row.name, row.amount, row.date]),
          ],
        },
      },
    ],
  };

  const pdfDocGenerator = pdfMake.createPdf(docDef);
  pdfDocGenerator.getBlob((blob: Blob) => {
    postMessage(blob);
  });
});
downloadReport() {
  const worker = new Worker(
    new URL('./pdf-generator.worker', import.meta.url),
    {
      type: 'module',
    },
  );

  worker.onmessage = ({ data }) => {
    const blob = data as Blob;
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'Report.pdf';
    a.click();
    URL.revokeObjectURL(url);
    worker.terminate();
  };

  const payload = {
    title: 'Monthly Finance Report',
    tableData: this.tableData,
  };

  worker.postMessage(payload);
}
}

Function-Based Route Redirects

in default route in earlier version we used to pass router name to redirect now it supports function as well

import { redirectByRole } from './service/utility'; { path:'', redirectTo:
redirectByRole(), pathMatch:'full' }
export function redirectByRole(): string {
  const loggedData = localStorage.getItem('role');
  if (loggedData == 'admin') {
    return 'life-cycle';
  } else {
    return 'view-encap';
  }
}

computed signal

you can pass a signal to change another signal value based on one signal value change, reactive signal it's value will depend on value change on signal we pass. We can't changed computed signal value it will change if dependent value changes doubleCount signal value will change after 5 sec

count = signal<number>(2);
doubleCount = computed(() => this.count() * 2);

constructor() {
  setTimeout(() => {
    this.count.set(5);
  }, 5000);
}
}

Linked Signals

linked signals are reactive variables that depend on other signals and update automatically when their dependencies change: like computed values. in source you can pass which signal we need to track if source changes then only it will compute again total signal state value will change after 8 sec only as in source that value is there

firstNum = signal<number>(5);
secondNum = signal<number>(7);
total = linkedSignal({
  source: this.firstNum,
  computation: () => this.firstNum() + untracked(this.secondNum),
});

constructor() {
  setTimeout(() => {
    this.firstNum.set(6);
  }, 8000);
  setTimeout(() => {
    this.secondNum.set(10);
  }, 10000);
}
}

Change Detection

Core concepts and implementation techniques in modern Angular.

What is a component in Angular?

A component is the fundamental building block of an Angular application. It controls a section of the UI. It can have multple files as A TypeScript class (logic) HTML template (view) optional CSS style file

  • A TypeScript class (logic)
  • HTML template (view)
  • optional CSS style file

What is Standalone Component ?

Standalone components are a new type of Angular component that does not need to be declared in a NgModule . These are components that can be used directly in the template of another component without being part of an NgModule, or imported in an NgModule. We Define standalone component by adding standalone field as true From Angular 16 we got standalone components From 18 By Defyalt all Components are standalone

  • Standalone components are a new type of Angular component that does not need to be declared in a NgModule . These are components that can be used directly in the template of another component without being part of an NgModule, or imported in an NgModule.
  • We Define standalone component by adding standalone field as true
  • From Angular 16 we got standalone components
  • From 18 By Defyalt all Components are standalone
@Component({ selector: 'app-add-user', templateUrl: './add-user.component.html',
standalone: true, styleUrl: './add-user.component.css' })

purpose of the @ Component decorator?

The @Component decorator tells Angular that the class is a component, in component decorator we have so many options like selector: Defines the custom HTML tag used to embed the component. template: Inline HTML content for the component. templateUrl: Path to an external HTML file for the component template. styles: Inline CSS styles applied specifically to this component. styleUrls: Path(s) to external CSS/SCSS files for styling the component. providers: Registers services at the component level (local DI). encapsulation: Controls style encapsulation (Emulated, None, or ShadowDom). changeDetection: Sets the change detection strategy (Default or OnPush). imports: (Standalone only) Modules/components this standalone component depends on. standalone: (Angular 14+) Declares this component as standalone (not part of a module).

  • selector: Defines the custom HTML tag used to embed the component.
  • template: Inline HTML content for the component.
  • templateUrl: Path to an external HTML file for the component template.
  • styles: Inline CSS styles applied specifically to this component.
  • styleUrls: Path(s) to external CSS/SCSS files for styling the component.
  • providers: Registers services at the component level (local DI).
  • encapsulation: Controls style encapsulation (Emulated, None, or ShadowDom).
  • changeDetection: Sets the change detection strategy (Default or OnPush).
  • imports: (Standalone only) Modules/components this standalone component depends on.
  • standalone: (Angular 14+) Declares this component as standalone (not part of a module).

Data Binding in Component

One Way Data Binding Two Way Data Binding From .ts to .html From .html to .ts Using ngModel Property Binding Interpolation Event Binding

  • Property Binding
  • Interpolation
  • Event Binding

@Input() and @ Output() decorators and how are they used?

@ Input(): Pass data from parent to child @Output(): Send event/data from child to parent

  • @ Input(): Pass data from parent to child
  • @Output(): Send event/data from child to parent

Various ways of component communication

To pass data from one component to another there are following ways based on scenario

  • using Input and Output.
  • using viewChild.
  • using service by storing value into variables.
  • using Subject & SubjectBehaviour of RXJS.
  • using routing i.e. navigating from one component to another with query param.
  • store data in local Storage or session Storage and read data wherever we need.

What is Design Pattern

Design patterns are proven solutions to common software design problems. we often use them to keep my Angular applications scalable and maintainable.

  • Singleton pattern is naturally used in Angular services (when provided in root).
  • Observer pattern is the core of RxJS: I use it to react to real-time data, like user auth status or notifications.
  • I use the Strategy pattern when I want interchangeable behaviors: like different login or payment methods.
  • Angular’s decorators like @Component and @Injectable are based on the Decorator pattern, which adds metadata and behavior to classes.
  • I’ve also used the Factory pattern for dynamic service creation based on app config.

Singleton Pattern

A class has only one instance throughout the app Angular services by default follow Singleton pattern when provided in root. This ensures there’s only one instance shared across the app unless scoped differently.

Factory Pattern

Create objects without exposing the creation logic to the client. Sometimes I use factories to create service instances dynamically: for example, selecting a payment gateway like RazorPay or Stripe based on config at runtime.

{ provide: PaymentService, useFactory: (configService: ConfigService) => {
return configService.useStripe ? new StripeService() : new RazorPayService(); },
deps: [ConfigService] }

Observer Pattern

Allows subscribing to state changes. Angular uses RxJS which is built around the Observer pattern. Components subscribe to observables from services to react to data changes

Strategy Pattern

When I need different behavior based on conditions, I define strategies and inject the right one. Like choosing between different login mechanisms: Google, Facebook, Email LoginStrategy interface → EmailLogin, GoogleLogin,

Decorator Pattern

Adds additional behavior to an object without altering its structure. Angular decorators like @Component, @Injectable, and @Directive are actual examples of the decorator pattern. They attach metadata and behavior to classes.

What are directives

Directives add behaviour to an existing DOM element or an existing component instance. Multple directives can be applied to single DOM element. It inform how DOM Element should behave

  • Like in case of *ngIf it add or remove element
  • by ngClass or ngStyle it add dynamic class or css to DOM Element
  • ngModel sync variable with texbox value

Types of Directives

Structural Directive - responsible to add or remove elements from DOM *ngIf , *ngFor. Attribute Directive - responsible to add extra behavior to existing element - ngClass, ngStyle Component - directive with a template Custom Directive - you can create custom directive by cli command ng generate directive directive_name

  • Structural Directive - responsible to add or remove elements from DOM *ngIf , *ngFor.
  • Attribute Directive - responsible to add extra behavior to existing element - ngClass, ngStyle
  • Component - directive with a template
  • Custom Directive - you can create custom directive by cli command ng generate directive directive_name

What is Structural Directive ?

Structural Directive are responsible to change structure of DOM, means they can add or remove element from DOM or Create New Elements also.

*ngIf Directive ?

*ngIf => needs Boolean value if true will render element or it will remove. we can pass variable, comparsion or function as well

isDiv1Visiable: boolean = true;

div2Status: string = 'Show';

getDiv1VisibilityStatus() {
  return this.isDiv1Visiable;
}

get getDiv2Status(): string {
  return this.div2Status;
}
}
<div *ngIf="isDiv1Visiable == true" class="bg-success p-3">Way - 1</div>
<div *ngIf="div2Status == 'Show'" class="bg-danger p-3">Way - 2</div>
Way-3
<tr *ngFor="let item of studentList;let sr = index">
  <td>{{item.name}}</td>
  <td>{{item.mobile}}</td>
  <td>
    <span *ngIf="item.isActive == true">Active</span>
    <span *ngIf="item.isActive == false">De-active</span>
  </td>
</tr>
<p *ngIf="getDiv1VisibilityStatus()">way -4</p>
<p *ngIf="getDiv2Status">way 5 using getter method</p>

*ngFor Directive ?

*ngFor =>Helps to iterate on Array / Helps to create Dynamic Element

cityList = ['Nagpur', 'Delhi', 'Noida', 'Mumbai', 'Pune'];

studentList = [
  { isActive: false, name: 'AAA', city: 'pune', mobile: '99887766' },
  { isActive: false, name: 'BBB', city: 'Mumbai', mobile: '1111111' },
  { isActive: false, name: 'CCC', city: 'Solpur', mobile: '2222222' },
  { isActive: false, name: 'DDD', city: 'Panji', mobile: '777777' },
];

getStudentList() {
  return this.studentList.filter((m) => m.isActive == true);
}
}
<ul>
  <li *ngFor="let item of cityList">{{item}}</li>
</ul>

<button class="btn btn-success" *ngFor="let city of cityList">{{city}}</button>

<tr *ngFor="let item of studentList;let sr = index">
  <td>{{ sr+1}}</td>
  <td>{{item.name}}</td>
  <td>{{item.mobile}}</td>
</tr>

<ul>
  <li *ngFor="let item of getStudentList()">{{item.name}}</li>
</ul>

*ngSwitch Directive ?

Render Element based on Multiple conditionss

startingCharDayName: string = 'tue';
<div [ngSwitch]="startingCharDayName">
  <span *ngSwitchCase="'mon'">Monday</span>
  <span *ngSwitchCase="'tue'">tuesday</span>
  <span *ngSwitchCase="'wed'">wednesday</span>
  <span *ngSwitchCase="'thu'">Thursday</span>
  <span *ngSwitchDefault>No Value</span>
</div>

How to Create Custom Directive

According to requirement we Create Custom Directive with Our own set of rules and Behaviour

ng generate directive highlight => cmd to create Directive import { Directive,
ElementRef, HostListener, Input } from '@angular/core'; @Directive({ selector:
'[appHoverHighlight]' }) export class HoverHighlightDirective {
constructor(private el: ElementRef) {} @HostListener('mouseenter')
onMouseEnter() { this.el.nativeElement.style.backgroundColor = 'yellow'; }
@HostListener('mouseleave') onMouseLeave() {
this.el.nativeElement.style.backgroundColor = 'black'; } }
ng generate directive numbersOnly @Directive({ selector: '[appNumbersOnly]' })
export class NumbersOnlyDirective { constructor() { } @HostListener('keypress',
['$event']) onKeyPress(event: KeyboardEvent) { const charCode = event.which ?
event.which : event.keyCode; if (charCode < 48 || charCode > 57) {// Allow: 0-9
only event.preventDefault(); } } }
ng generate directive tooltip @Input('appTooltip') tooltipText: string = '';
tooltip: HTMLElement | null = null; constructor(private el: ElementRef, private
renderer: Renderer2) { } @HostListener('mouseenter') onMouseEnter() { if
(!this.tooltip) { this.tooltip = this.renderer.createElement('span');
this.tooltip.innerText = this.tooltipText;
this.renderer.appendChild(document.body, this.tooltip);
this.renderer.setStyle(this.tooltip, 'position', 'fixed');
this.renderer.setStyle(this.tooltip, 'background-color', '#333');
this.renderer.setStyle(this.tooltip, 'color', '#fff');
this.renderer.setStyle(this.tooltip, 'padding', '5px 10px');
this.renderer.setStyle(this.tooltip, 'border-radius', '4px');
this.renderer.setStyle(this.tooltip, 'font-size', '12px');
this.renderer.setStyle(this.tooltip, 'z-index', '1000'); } }
@HostListener('mousemove', ['$event']) onMouseMove(event: MouseEvent) { if
(this.tooltip) { this.renderer.setStyle(this.tooltip, 'top', (event.clientY +
10) + 'px'); this.renderer.setStyle(this.tooltip, 'left', (event.clientX + 10) +
'px'); } } @HostListener('mouseleave') onMouseLeave() { if (this.tooltip) {
this.renderer.removeChild(document.body, this.tooltip); this.tooltip = null; } }
ng generate directive hasRole @Directive({ selector: '[appHasRole]' }) export
class HasRoleDirective { constructor(private el: ElementRef) { const loggedRole
= localStorage.getItem('loggedInRole'); if (loggedRole !== 'Admin') {
this.el.nativeElement.style.display = 'none' } } }

What is ElementRef in Angular?

ElementRef allows you to interact you with dom Element, it gets you instance of DOM element.

  • ElementRef is a wrapper around the native DOM element.
  • It gives you direct access to the HTML element in your directive or component.
  • You can change style, class, content, attributes,Read or modify element attributes
  • What we do in JavaScript with Document.getElementById , some of it we can do with ElementRef

What is Renderer2?

It's a service which allows you to manipulate DOM

  • setStyle -Set CSS styles (this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'red');)
  • removeStyle, addClass,createElement, appendChild

What is @HostListener?

allows you to listen to DOM events on the element where your directive or component

  • whenever event happens on my host element, it execute method
  • It's a decorator provided by Angular.
  • Instead of manually adding event listeners (like addEventListener), Angular handles it for you safely and cleanly.
@HostListener(eventName: string, [args: string[]]) methodName(args) { // handle
event } @HostListener('mouseenter') onMouseEnter() {
this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow'); }
@HostListener('mouseleave') onMouseLeave() {
this.renderer.removeStyle(this.el.nativeElement, 'background-color'); }

purpose of trackBy in ngFor Directive

trackBy option in *ngFor allows you to specify a function that Angular can use to track items by a unique identifier instead of relying on object reference identity. This identifier helps Angular keep track of which items have changed, been added, or been removed when the data changes. It improves performance, especially when dealing with large lists, by avoiding unnecessary re-rendering of DOM elements.

Home Part 1

Check Youtube Video

Angular Lifecycle Hooks

Button Angular components and directives have a series of lifecycle phases from creation to destruction. Lifecycle hooks are special methods that allow you to write code onto these phases to execute custom logic.

ngOnChanges

Invoked when any data-bound input property changes. Reacting to changes in @Input() properties

  • Invoked when any data-bound input property changes.
  • Reacting to changes in @Input() properties
export class UserProfileComponent implements OnChanges {
  @Input() user!: User;

  ngOnChanges(changes: SimpleChanges) {
    if (changes['user']) {
      this.logUserChange(changes['user'].currentValue);
    }
  }

  private logUserChange(updatedUser: User) {
    console.log('User updated:', updatedUser);
  }
}

ngOnInit

Called once after the first ngOnChanges(). Initializing component properties, fetching data, or setting up subscriptions.​ change

  • Called once after the first ngOnChanges().
  • Initializing component properties, fetching data, or setting up subscriptions.​
export class UserProfileComponent implements OnChanges {
  @Input() user!: User;

  ngOnChanges(changes: SimpleChanges) {
    if (changes['user']) {
      this.logUserChange(changes['user'].currentValue);
    }
  }

  private logUserChange(updatedUser: User) {
    console.log('User updated:', updatedUser);
  }
}

ngDoCheck

The ngDoCheck lifecycle hook in Angular is a powerful tool for implementing custom change detection logic, especially when Angular's default change detection doesn't suffice. Means Lets say you have a reusable component with input and you are getting object in that Input now in parent component if you change variable value which you're passing as Input you ngOnChanges in Child component will execute but if you just changed one field value in parent component you ngOnchange won't execute in that case you can detect changes in ngDoCheck

export class MyButtonComponent implements DoCheck, OnChanges {
  @Input() user!: { name: string; age: number };
  private differ!: KeyValueDiffer<string, any>;

  constructor(private differs: KeyValueDiffers) {}

  ngOnChanges(changes: SimpleChanges): void {
    debugger;
    //this will trigger if in parent
  }
  ngOnInit() {
    this.differ = this.differs.find(this.user).create();
  }

  ngDoCheck() {
    const changes = this.differ.diff(this.user);
    if (changes) {
      changes.forEachChangedItem((item: any) => {
        console.log(
          'Property {item.key} changed from {item.previousValue} to {item.currentValue}',
        );
      });
    }
  }
}
<button (click)="updateUser()">change</button>
<app-my-button [user]="user"></app-my-button>
export class LifeCycleComponent { user = { name: 'Alice', age: 30 };
updateUser() { this.user.age = 31; }
export class LifeCycleComponent { user = { name: 'Alice', age: 30 };
updateUser() { this.user = { name: 'Alice', age: 50 }; }

ngAfterContentInit

Called after Angular projects external content into the component's view. Accessing projected content using ContentChild() or ContentChildren()​

  • Called after Angular projects external content into the component's view.
  • Accessing projected content using ContentChild() or ContentChildren()​
export class ContentWrapperComponent implements AfterContentInit {
  @ContentChild('projectedContent') content!: ElementRef;

  ngAfterContentInit() {
    console.log('Projected content:', this.content.nativeElement.textContent);
  }
}

ngAfterContentChecked

Called after every check of projected content. Responding to changes in projected content.​

  • Called after every check of projected content.
  • Responding to changes in projected content.​
export class ContentCheckerComponent implements AfterContentChecked {
  ngAfterContentChecked() {
    console.log('Projected content checked.');
  }
}

ngAfterViewInit

Called after Angular initializes the component's views and child views. Accessing ViewChild() or ViewChildren() properties.​ or DOM Interactions: Access and manipulate DOM elements only after the view has been initialized

  • Called after Angular initializes the component's views and child views.
  • Accessing ViewChild() or ViewChildren() properties.​
  • or DOM Interactions: Access and manipulate DOM elements only after the view has been initialized
export class ViewInitComponent implements AfterViewInit {
  @ViewChild('paragraph') paragraph!: ElementRef;

  ngAfterViewInit() {
    console.log('Paragraph content:', this.paragraph.nativeElement.textContent);
  }
}

ngAfterViewChecked

Called after every check of the component's views and child views. Responding to changes in the component's view.​

  • Called after every check of the component's views and child views.
  • Responding to changes in the component's view.​
export class ViewCheckerComponent implements AfterViewChecked {
  ngAfterViewChecked() {
    console.log('Component view checked.');
  }
}

ngOnDestroy

Cleanup just before Angular destroys the component Unsubscribing from observables, detaching event handlers, or other cleanup tasks.​​

  • Cleanup just before Angular destroys the component
  • Unsubscribing from observables, detaching event handlers, or other cleanup tasks.​​
export class CleanupComponent implements OnDestroy {
  private subscription!: Subscription;

  constructor(private dataService: DataService) {
    this.subscription = this.dataService.getData().subscribe();
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
    console.log('Component destroyed and subscription unsubscribed.');
  }
}

Deferrable Views

a new way to delay or defer the rendering of parts of your template until certain conditions are met, such as:

  • When the component is visible in the viewport
  • when some event triggered
  • After an idle period
  • After a timeout
  • this helps to => Speeding up initial load, Saving resources by not rendering offscreen content,Improving performance in large Angular apps
@defer (on idle) {
<app-heavy-component></app-heavy-component>
} @placeholder {
<p>Loading component...</p>
}
@defer (on interaction) {
<app-recent-activity></app-recent-activity>
} @placeholder {
<p>Waiting for user activity...</p>
}
@defer (on viewport) {
<app-footer-info></app-footer-info>
} @placeholder {
<p>Footer loading...</p>
}

Built-in Control Flow Syntax (@if, @for, @switch)

Angular 17 introduced a built-in control flow syntax using the @ block syntax (@if, @for, @switch), replacing the traditional structural directives (*ngIf, *ngFor, *ngSwitch). It offers better performance, type narrowing, and cleaner syntax without needing to import CommonModule.

  • @if / @else if / @else: Direct conditional branching with automatic TypeScript type narrowing in template expressions.
  • @for: Iterative loops with a mandatory track expression for optimal DOM reconciliation and built-in @empty block for empty arrays.
  • @switch / @case / @default: Simplified multi-condition branching with clean block structure.
  • No need to import CommonModule (NgIf, NgFor, NgSwitch) in standalone components.
@if (isLoggedIn) {
<p>Welcome back, {{ username }}!</p>
} @else if (isGuest) {
<p>Browsing as Guest</p>
} @else {
<button (click)="login()">Login</button>
}
<ul>
  @for (user of users; track user.id; let idx = $index) {
  <li>{{ idx + 1 }}. {{ user.name }}</li>
  } @empty {
  <li>No users found.</li>
  }
</ul>
@switch (role) { @case ('admin') {
<app-admin-panel />
} @case ('editor') {
<app-editor-panel />
} @default {
<app-user-panel />
} }

@let declare variables directly within templates

@let, was introduced to declare variables directly within templates. This enhances template logic and readability by allowing the creation of aliases for complex expressions or values that may be used multiple times.

studentLst: any[] = [
  { fname: 'sachin', lname: 'Tendulkar' },
  { fname: 'sachin', lname: 'Tendulkar' },
  { fname: 'sachin', lname: 'Tendulkar' },
];
}
@for (item of studentLst; track $index) { @let fullName = item.fname + '
'+item.lname;
<li>{{fullName}}</li>
}

Function-Based Route Redirects

in default route in earlier version we used to pass router name to redirect now it supports function as well

import { redirectByRole } from './service/utility'; { path:'', redirectTo:
redirectByRole(), pathMatch:'full' }
export function redirectByRole(): string {
  const loggedData = localStorage.getItem('role');
  if (loggedData == 'admin') {
    return 'life-cycle';
  } else {
    return 'view-encap';
  }
}

computed signal

you can pass a signal to change another signal value based on one signal value change, reactive signal it's value will depend on value change on signal we pass. We can't changed computed signal value it will change if dependent value changes doubleCount signal value will change after 5 sec

count = signal<number>(2);
doubleCount = computed(() => this.count() * 2);

constructor() {
  setTimeout(() => {
    this.count.set(5);
  }, 5000);
}
}

Linked Signals

linked signals are reactive variables that depend on other signals and update automatically when their dependencies change: like computed values. in source you can pass which signal we need to track if source changes then only it will compute again total signal state value will change after 8 sec only as in source that value is there

firstNum = signal<number>(5);
secondNum = signal<number>(7);
total = linkedSignal({
  source: this.firstNum,
  computation: () => this.firstNum() + untracked(this.secondNum),
});

constructor() {
  setTimeout(() => {
    this.firstNum.set(6);
  }, 8000);
  setTimeout(() => {
    this.secondNum.set(10);
  }, 10000);
}
}

Zoneless Change Detection

zoneless change detection mechanism that eliminates the need for zone.js, leading to improved performance and better debugging capabilities. means now you can disable change detection overall project

bootstrapApplication(App, {
  providers: [provideExperimentalZonelessChangeDetection()],
});

input signal

let's say we created a reusable component and instaed of normal input , we can create a signal based input for more reactivity.

@Component({
  selector: 'app-my-card',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MyCardComponent {
  @Input() htmlTemplate!: TemplateRef<Element>;
  @Input() showFillter: boolean = false;
  cardTitle = input<string>(); //signal input
}
<div class="card">
  <div class="card-header bg-primary">{{cardTitle()}}</div>
  <div class="card-body">
    <ng-container [ngTemplateOutlet]="htmlTemplate"></ng-container>
  </div>
  @if (showFillter) {
  <div class="card-footer"></div>
  }
</div>

Reactive form control state change events

Angular forms now expose a property called events, which allows you to subscribe to a stream of events for this form control. can track changes in value, touch state, pristine status, and the control status.

const nameControl = new FormControl<string | null>('name', Validators.required);
nameControl.events.subscribe((event) => {
  // process the individual events
});

resource api

resource() is a new reactive way that helps manage asynchronous data (like HTTP calls, promises, or observables) in a declarative, signal-based way.

userResourceData = resource({
  loader: () => {
    return fetch('https://jsonplaceholder.typicode.com/users').then((result) =>
      result.json(),
    );
  },
});
@if (userResourceData.isLoading()) {
<span>Loading</span>
} @if (userResourceData.hasValue()) {
<ul>
  @for (item of userResourceData.value(); track $index) {
  <li>{{item.username}}</li>
  }
</ul>
}

rxResource api

rxResource() we can use in case of observables

user = rxResource({
  loader: () => this.userService.getUsers(),
});
@if (user.isLoading()) {
<span>Loading</span>
} @if (user.hasValue()) { @for (item of userResourceData.value(); track $index)
{
<span>{{item.username}}</span> , } }

how to create a proxy to solve CORS

Browsers enforce the Same-Origin Policy, which restricts web pages from making requests to a different domain than the one that served the web page. This policy enhances security but can lead to CORS errors when your frontend application tries to access resources from a different origi To bypass CORS restrictions, you can set up a proxy server that acts as an intermediary between your frontend application and the external API. Since CORS policies are enforced by browsers and not servers, the proxy server can make requests to the external API and relay the responses back to the frontend. create a file named proxy.conf.json. In your angular.json file

{ "/api": { "target": "https://projectapi.gerasim.in", "secure": true,
"changeOrigin": true, "logLevel": "debug" } }
"serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": {
"proxyConfig": "proxy.conf.json" },
getData() {
  this.http.get('/api/BankLoan/GetAllUsers').subscribe((result: any) => {
    console.log(result);
  });
}
}
getData() {
  this.http.get('/api/BankLoan/GetAllUsers').subscribe((result: any) => {
    console.log(result);
  });
}
}

polyfills

polyfills are scripts that add support for features that are not natively available in all browsers. They essentially "fill in the gaps" to ensure your Angular app works consistently across different environments. Polyfills help bridge this gap by emulating modern features in older browsers. Array.includes()

  • Different browsers support different features of JavaScript and web APIs. For example:
  • Modern browsers support ES6+ features.
  • Older versions (like Internet Explorer 11) do not.
const isValid = [1, 2, 3].includes(2);
import 'core-js/es/array/includes';

Hot and Cold Observables in RxJS

A cold observable creates a new execution for each subscriber: like an HTTP call. A hot observable shares the same data stream among all subscribers: like fromEvent or a Subject. In Angular, cold observables are common for APIs, but we can convert them to hot ones using shareReplay when caching is needed.

  • Starts emitting only when someone subscribes.
  • Each subscriber gets separate data streams.
  • Each subscriber gets a different random value.
  • Subscribers share the same source. It emits even if no one is listening yet.
  • All subscribers share the same data source.
const cold$ = new Observable((observer) => {
  console.log('New Observable execution');
  observer.next(Math.random());
});
const hot$ = new Subject();

Debouncing Using Rxjs Operator

in.ts in.html debounceTime => it will wait for particular time the it will execute distinctUntilChanged=> it will check if old value is same or not - in case of copy paste we can have same value pasted again so it should not trigger api call switchMap => if existing api call going on & before response came another api call trigger it will cancel previous api call filter => it's checking if min 3 char is there then only it will trigger api call

  • debounceTime => it will wait for perticular time the it will execute
  • distinctUntilChanged=> it will check if old value is same or not - in case of copy paste we can have same value pasted again so it should not trigger api call
  • switchMap => if existing api call going on & before response came another api call trigger it will cancel previous api call
  • filter => it's checking if min 3 char is there then only it will trigger api call
searchControl: FormControl = new FormControl('');
<input type="text" [formControl]="searchControl" placeholder="serch" />
constructor(private http: HttpClient) {
  this.searchControl.valueChanges
    .pipe(
      debounceTime(10),
      distinctUntilChanged(),
      filter((search: string) => search.trim().length > 3),
      switchMap((res: string) =>
        this.http.get(
          'https://jsonplaceholder.typicode.com/users?search=' + res,
        ),
      ),
    )
    .subscribe((response: any) => {
      console.log(response);
    });
}
}

Various ways to create observables

Here we get City Array Here Subscribe block will execute for every element in array, each time we will get single element like for loop this is like setInterval from javaScript, if will emit data after 1 sec subscribe will execuite after 1 sec

cityList$ = of(['Pune', 'Nagpur', 'Mumbai', 'Thane']);
this.cityList$.subscribe((res: string[]) => {
  console.log(res);
});
mhCityList$ = from(['Pune', 'Nagpur', 'Mumbai', 'Thane']);
this.mhCityList$.subscribe((res: string) => {
  console.log(res);
});
timeInterval$ = interval(1000);
this.timeInterval$.subscribe((res: number) => {
  console.log(res);
});

takeUntil operator

we create inverval and another observable after 5 sec we are emiting another obserable so interval will stop after that

timeInterval$ = interval(1000);
stopInterval$ = new Subject<void>();
this.timeInterval$.pipe(takeUntil(this.stopInterval$)).subscribe((res) => {
  console.log(res);
  if (res == 5) {
    this.stopInterval$.next();
  }
});

take (number)

create a interval which emits value after 1 sec here we use take so after 3rd it will stop receiving data, its like unsubscribe

timerInterval = interval(1000);
this.timerInterval.pipe(take(3)).subscribe((timer: number) => {
  console.log(timer);
});

delay

just Like setTimeout

login(uName: string, pwd: string): Observable<any> {
  console.log('login login');
  return of({ userId: 123, uName: 'test', uPwd: '11223' }).pipe(delay(5000));
}
}

combine Multple observable=> forkJoin, mergeMap

Core concepts and implementation techniques in modern Angular.

mhCityArray = of(['Pune', 'Nagpur', 'Mumbai', 'Thane']);
mpCityArray = of(['Bhopal', 'Indore']);
forkJoin([this.mhCityArray, this.mpCityArray]).subscribe((result: any) => {
  console.log(result[0]);
  console.log(result[1]);
});
getUsers() {
  this.producSrv
    .getUsers()
    .pipe(mergeMap((res: any) => this.producSrv.getUserById(res[0].id)))
    .subscribe((result) => {});
}
}

combineLatest, combineLatestWith

Both will Behave same

stateFilter: FormControl = new FormControl('');
cityFilter: FormControl = new FormControl('');
<select [formControl]="stateFilter">
  <option value="">Select</option>
  <option value="MAH">MAH</option>
  <option value="GOA">GOA</option>
</select>
<select [formControl]="cityFilter">
  <option value="">Select</option>
  <option value="Nagpur">Nagpur</option>
  <option value="Panji">Panji</option>
</select>
const stateFilter$ = this.stateFilter.valueChanges; const cityFilter$ =
this.cityFilter.valueChanges;
combineLatest([stateFilter$,cityFilter$]).subscribe((res:any)=>{ this will
execute when both dropdown has value })
stateFilter$.pipe( combineLatestWith(cityFilter$) ).subscribe((result:any)=>{
this will execute when both dropdown has value })

switchMap | mergeMap | concatMap | exhaustMap

🌀 switchMap: The Smart Replacer It cancels the previous inner observable whenever a new one comes in. Useful when you only care about the latest emitted value. ✅ Great for: Autocomplete, live search, debounced input 🌊 mergeMap: The Parallel Player It subscribes to all inner observables concurrently. Doesn’t cancel anything, so all requests will go through. ✅ Great for: Making multiple API calls in parallel No control over completion order 🚶‍♂️concatMap: The Queue Follower Waits for each inner observable to complete before subscribing to the next. Preserves order Great for: Step-by-step processes-Save form => Upload image => Send confirmation 🚫 exhaustMap: The Gatekeeper Ignores new emissions if there’s an active inner observable. Good for avoiding duplicate actions: like preventing multiple api calls. if one api call is in progress - it won't process another api call

  • It cancels the previous inner observable whenever a new one comes in.
  • Useful when you only care about the latest emitted value.
  • ✅ Great for: Autocomplete, live search, debounced input
  • It subscribes to all inner observables concurrently.
  • Doesn’t cancel anything, so all requests will go through.
  • ✅ Great for: Making multiple API calls in parallel
  • No control over completion order
  • Waits for each inner observable to complete before subscribing to the next.
  • Preserves order
  • Great for: Step-by-step processes-Save form => Upload image => Send confirmation
  • Ignores new emissions if there’s an active inner observable.
  • Good for avoiding duplicate actions: like preventing multiple api calls.
  • if one api call is in progress - it won't proccess another api call
const searchUrl = 'https://jsonplaceholder.typicode.com/users';
this.searchControl.valueChanges
  .pipe(
    switchMap((res: string) => this.http.get(this.searchUrl + 'search=' + res)),
  )
  .subscribe((response: any) => {
    console.log(response);
  });
const searchUrl = 'https://jsonplaceholder.typicode.com/users';
this.http
  .get(searchUrl)
  .pipe(switchMap((res: any) => this.http.get(searchUrl + '/' + res[0].id)))
  .subscribe((res: any) => {
    debugger;
  });
const searchUrl = 'https://jsonplaceholder.typicode.com/users';
from([2, 3, 4])
  .pipe(mergeMap((userid: number) => this.http.get(searchUrl + '/' + userid)))
  .subscribe((result: any) => {
    debugger;
  });
const searchUrl = 'https://jsonplaceholder.typicode.com/users';
this.http
  .get(searchUrl)
  .pipe(
    mergeMap((res: any) => from(res)), // emit users one by one
    mergeMap((user: any) => this.http.get(searchUrl + '/' + user.id)),
  )
  .subscribe((res: any) => {
    debugger;
  });
const searchUrl = 'https://jsonplaceholder.typicode.com/users';
this.http
  .get(searchUrl)
  .pipe(mergeMap((res: any) => this.http.get(searchUrl + '/' + res[0].id)))
  .subscribe((res: any) => {
    debugger;
  });
const searchUrl = 'https://jsonplaceholder.typicode.com/users';
from([2, 3, 4])
  .pipe(concatMap((userid: number) => this.http.get(searchUrl + '/' + userid)))
  .subscribe((result: any) => {
    debugger;
  });
loginClick$.pipe(exhaustMap(() => authService.login()));

Multiple API calls, Sequential one then next then next

Core concepts and implementation techniques in modern Angular.

const getAllUsers$ = this.http.get(
  'https://jsonplaceholder.typicode.com/users',
);
getAllUsers$
  .pipe(
    switchMap((user: any) =>
      this.http.get(
        'https://jsonplaceholder.typicode.com/comments?userid=' + user[0].id,
      ),
    ),
    switchMap((comment: any) =>
      this.http.get(
        'https://jsonplaceholder.typicode.com/replies?commentid=' +
          comment[0].id,
      ),
    ),
  )
  .subscribe((replies) => {});
const getAllUsers$ = this.http.get(
  'https://jsonplaceholder.typicode.com/users',
);
getAllUsers$
  .pipe(
    concatMap((user: any) =>
      this.http.get(
        'https://jsonplaceholder.typicode.com/comments?userid=' + user[0].id,
      ),
    ),
    concatMap((comment: any) =>
      this.http.get(
        'https://jsonplaceholder.typicode.com/replies?commentid=' +
          comment[0].id,
      ),
    ),
  )
  .subscribe((replies) => {});

on success of one api call trigger another api call with paramater from 1st api resposne and need both response

Core concepts and implementation techniques in modern Angular.

this.http
  .get('https://jsonplaceholder.typicode.com/users/1')
  .pipe(
    switchMap((userInfo) =>
      this.http
        .get('https://jsonplaceholder.typicode.com/roles/1')
        .pipe(map((roles: any) => ({ userData: userInfo, roles: roles }))),
    ),
  )
  .subscribe((res: any) => {});

What is SOLID

SOLID is an acronym for 5 design principles that make software designs more understandable, flexible, and maintainable.

  • S: Single Responsibility Principle (SRP): A class or component should have only one reason to change.
  • O: Open/Closed Principle (OCP): Software entities should be open for extension, but closed for modification.
  • L: Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types without breaking the application.
  • I: Interface Segregation Principle (ISP): Many client-specific interfaces are better than one general-purpose interface.
  • D: Dependency Inversion Principle (DIP): Depend upon abstractions (interfaces), not concrete implementations.

S => Single Responsibility Principle (SRP)

A class or component should have only one reason to change. A component, service, or class should do only one job. In Angular, I make sure my components only manage the UI, and all data-fetching or logic goes into services. That way, if I need to change the UI or logic, I don’t touch both places

  • Component handles view/UI logic only
  • Services handle business logic or HTTP logic
  • This separation makes your code testable and reusable.

O => Open/Closed Principle (OCP)

Software entities should be open for extension but closed for modification. If a class says it implements something, it should behave like it. In Angular, if I replace one service with another (like EmailService → SMSService), everything should still work, as long as both follow the same interface. If I need to support a new login method, I don't rewrite my AuthService. Instead, I plug in a new strategy or class: so old logic stays safe and unchanged.

  • In Angular: Use inheritance, interfaces, or strategy pattern to extend without modifying.
  • We should be able to extend functionality without rewriting old code.
interface AuthStrategy {
  login(): Observable<any>;
}
class GoogleLogin implements AuthStrategy {
  login() {
    /* google logic */
  }
}
class FacebookLogin implements AuthStrategy {
  login() {
    /* fb logic */
  }
}

L => Liskov Substitution Principle (LSP)

Derived classes must be substitutable for their base class. Classes/components should only depend on what they actually use. Instead of having one big user interface with every possible field, I split them based on use-case: like Profile info, Address info, Cart info: so each module only depends on what it needs.

  • In Angular: Use interface-based DI: ensure derived services behave like the base class/interface.
interface NotificationService {
  send(msg: string): void;
}

class EmailService implements NotificationService {
  send(msg: string) {
    console.log('email sent');
  }
}
class SMSService implements NotificationService {
  send(msg: string) {
    console.log('SMS sent');
  }
}

I => Interface Segregation Principle (ISP)

Clients should not be forced to depend on interfaces they don’t use. High-level parts of the app shouldn’t be tightly tied to low-level details. I inject services using interfaces or tokens, not direct classes. That way, I can swap APIs or services without touching the components that use them.”

  • In Angular: Create focused interfaces, not "God interfaces".
  • Now different components/services can use only what they need
interface User {
  name: string;
  email: string;
  password: string;
  address: string;
  cart: string[];
}
interface UserProfile {
  name: string;
  email: string;
}
interface AddressInfo {
  address: string;
}
interface CartInfo {
  cart: string[];
}

D: Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces or abstract classes). In Angular, DIP is naturally achieved through Hierarchical Dependency Injection.

  • High-level components/services depend on abstract class tokens rather than concrete service classes.
  • Allows easily swapping implementations (e.g., MockService for testing, ProductionService for live app).
  • Configured in providers array using { provide: LoggerService, useClass: ConsoleLoggerService }.
// Abstraction
export abstract class LoggerService {
  abstract log(message: string): void;
}

// Low-level Implementation
@Injectable()
export class ConsoleLoggerService implements LoggerService {
  log(message: string): void {
    console.log('[Console]: ' + message);
  }
}

// High-level Consumer depends on abstraction
@Component({
  selector: 'app-user',
  providers: [{ provide: LoggerService, useClass: ConsoleLoggerService }],
})
export class UserComponent {
  constructor(private logger: LoggerService) {
    this.logger.log('UserComponent initialized');
  }
}

What to Say in an Interview

In Angular, I apply SOLID principles daily. For example, I keep components focused on view logic (SRP), use strategy patterns and interfaces to switch services without modifying existing code (OCP & DIP), and write lean interfaces specific to the context (ISP). Angular’s DI system makes it easy to follow these principles and build scalable apps."

use of map Operator

consider api response like this

[
  {
    id: 1,
    name: 'Leanne Graham',
    username: 'Bret',
    email: 'Sincere@april.biz',
  },
  { id: 2, name: 'Ervin Howell', username: 'Ervin', email: 'Ervin@april.biz' },
];
getUsers() {
  this.http
    .get('https://jsonplaceholder.typicode.com/users')
    .pipe(
      map((userList: any) =>
        userList.map((user: any) => {
          return { id: user.id, name: user.name };
        }),
      ),
    )
    .subscribe((res: any) => {
      //here you get array of differnt obj
      // [ {id:1,name: 'Leanne Graham'}, {id:2,name: 'Ervin Howell'} ]
    });
}
}
getUsers2() { this.http.get("https://jsonplaceholder.typicode.com/users").pipe(
map((userList:any)=> userList.map((user:any)=> ({id:user.id,name: user.name})))
).subscribe((res:any)=>{ without using return keyword // [ {id:1,name: 'Leanne
Graham'}, {id:2,name: 'Ervin Howell'} ] }) }

use of map Operator with array filter method

Core concepts and implementation techniques in modern Angular.

studentList$ = of(['Chetan', 'Rahul', 'Rakul', 'Arnav']);
this.studentList$
  .pipe(map((m: any) => m.filter((name: string) => name.startsWith('R'))))
  .subscribe((res: string[]) => {
    console.log(res);
  });
interval(1000)
  .pipe(
    filter((val) => val % 2 === 0), // only even numbers
  )
  .subscribe((val) => console.log('Even:', val));
this.searchControl.valueChanges
  .pipe(filter((text) => text.length >= 3))
  .subscribe((value) => {
    // Trigger API call here
  });

tap operator

but here we are extracting only fddMeasurements but storing whole response in service variable api full Response

originalData: any;
getTestById() {
  return this.http
    .get('https://projectapi.gerasim.in/api/SoilTest/GetTestById?id=1')
    .pipe(
      tap((response) => {
        //here we catch original data
        this.originalData = response;
      }),
      //then we modify data and send it back to subscriber
      map((result: any) => result.fddMeasurements),
    );
}
}
{ "testDate": "2025-01-30T07:15:13.587", "remarks": "strin", "approvalStatus":
"string", "createdDate": "2025-01-30T07:15:13.587", "fddMeasurements": [ {
"measurementId": 5, "testId": 1, "excavatedHoleVolume": 1.01, "wetSoilWeight":
1.01 }, { "measurementId": 21, "testId": 1, "excavatedHoleVolume": 2.01,
"wetSoilWeight": 2.01 } ] }

Subject & BehaviorSubject

Both Are are Multicasting Observables Means They allow multiple subscribers to listen to the same Observable stream. However, they differ in how they handle emissions, especially initial values and replay behavior.

subject

How to Create Subject how to use Next and Subscrbe

userName$ = new Subject<string
  >; fullName$ = new Subject<void>
    => when No data Emit; lastName : Subject<string>
      = new Subject<string
        >(); middleName = new Subject<string
          >(); If you Try to Initialize middleName = new Subject<string
            >("Demo") => Error Expected 0 arguments, but got 1;</string
          ></string
        ></string
      ></string
    ></void
  ></string
>
this.lastName.next('Jogi');
this.userName$.next('chetan11');
this.lastName.subscribe((value: string) => {
  console.log(value);
});

BehaviorSubject

How to Create BehaviorSubject how to use Next and Subscrbe

courseName = new BehaviorSubject<string>('Angular');
courseFees$ = new BehaviorSubject<number>(1200);
this.courseName.next('React');
this.courseName.subscribe((value: string) => {
  console.log(value);
});

shareReplay

How to implement caching of API calls

private userDetailCache = new Map<number, Observable<any>>();
}
getCacheUserId(id: number) {
  if (!this.userDetailCache.has(id)) {
    const product$ = this.http
      .get('https://jsonplaceholder.typicode.com/users/' + id)
      .pipe(shareReplay(1));
    this.userDetailCache.set(id, product$);
  }
  return this.userDetailCache.get(id)!;
}
}

What is Unit Testing in Angular, and why is it important?

Unit testing in Angular is the process of testing individual pieces of code, like components, services, pipes, or directives, in isolation. The main goal is to ensure that each unit of the app works as expected independently.

  • Detect bugs early before integration
  • Improve code reliability and confidence in refactoring
  • Acts as documentation for how each unit should behave
  • Jasmine – for writing the test specs
  • Karma – as the test runner (executes the tests in a browser environment)

What is TestBed in Angular?

TestBed is the primary Angular testing utility that allows you to create an Angular testing module, like a mini Angular app, just for the test environment.

  • Declare components, pipes, and directives you want to test.
  • Provide services or mocks.
  • Configure module-level setup (like imports, declarations, providers).
beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [CounterComponent],
  });

  fixture = TestBed.createComponent(CounterComponent);
  component = fixture.componentInstance;
  fixture.detectChanges(); // Trigger initial data binding
});

What is beforeEach in Angular Unit Testing

In Jasmine, beforeEach() is a special function that runs before each test case (it()) in a describe block. Think of it as a setup step to prepare your test environment before each individual test runs.

  • Avoid repeating code (like component setup or service creation).
  • Ensure each test starts with a fresh, clean state

What is describe in Jasmine

describe() is a test suite: it’s a way to group related test cases (it() blocks) together under a common description. It helps organize your tests into logical blocks, so when you run them in Karma, you can easily see which group of tests passed or failed.

  • xdescribe() : Skip the whole suite
  • xit() – Skip just one test
  • fdescribe() – Focus only this suite

View Encapsulation Modes

View Encapsulation determines how styles defined in a component are scoped. Angular provides three encapsulation modes: Emulated (default), ShadowDom, and None.

  • ViewEncapsulation.Emulated (Default): Simulates Shadow DOM by adding unique attributes (_ngcontent-c12) to HTML elements and scoping CSS selectors.
  • ViewEncapsulation.ShadowDom: Uses the browser's native Shadow DOM API to achieve true isolation.
  • ViewEncapsulation.None: Styles are injected into the global document <head> without scoping, affecting the whole page.
@Component({
  selector: 'app-custom-card',
  templateUrl: './custom-card.component.html',
  styleUrls: ['./custom-card.component.css'],
  encapsulation: ViewEncapsulation.Emulated,
})
export class CustomCardComponent {}

What Is Shadow DOM

Shadow DOM is a web standard that enables encapsulation of a component's internal structure and styling, ensuring that its internal DOM and CSS are isolated from the rest of the document. This means styles defined within a Shadow DOM do not affect the main document, and vice versa.​

encapsulation: ViewEncapsulation.Emulated (Default)

In Emulated mode, Angular simulates the behavior of the Shadow DOM by modifying the component's styles and template during compilation. It does this by:​

  • Adding Unique Attributes to Elements Angular appends unique attributes to the component's HTML elements. These attributes are typically in the form of _ngcontent-xyz, where xyz is a unique identifier.​
  • Modifying CSS Selectors:Angular updates the component's CSS selectors to target these unique attributes, ensuring that the styles apply only to the elements within the component.​
  • Create a reusable component, lets say Button component have a class added title on button
  • now in parent component create a class title with some color
  • in emulated case - title class wont get applied to button component
  • in none case title class from parent component n will get applied to child component as well
  • it means it will limit css within that component only

encapsulation: ViewEncapsulation.None

No encapsulation; styles are applied globally and can affect other components. To Test This Create a reusable component, lets say Button component have a class added title on button now in parent component create a class title with some color in none case title class from parent component n will get applied to child component as well it means it will limit css within that component only

  • No encapsulation; styles are applied globally and can affect other components.
  • Create a reusable component, lets say Button component have a class added title on button
  • now in parent component create a class title with some color
  • in none case title class from parent component n will get applied to child component as well
  • it means it will limit css within that component only

encapsulation: ViewEncapsulation.ShadowDom

Utilizes the browser's native Shadow DOM to encapsulate styles, providing true style isolation it won;t use any bootstrap or any global css as well, only css which are present in component To Test This Create a reusable component, lets say Button component have a class added title on button now in parent component create a class title with some color in none case title class from parent component n will get applied to child component as well it means it will limit css within that component only

  • Utilizes the browser's native Shadow DOM to encapsulate styles, providing true style isolation
  • it won;t use any bootstrap or any global css as well, only css which are present in component
  • Create a reusable component, lets say Button component have a class added title on button
  • now in parent component create a class title with some color
  • in none case title class from parent component n will get applied to child component as well
  • it means it will limit css within that component only

Real-Time Scenario

EmulatedComponent: Uses ViewEncapsulation.Emulated to ensure its styles don't leak out. ShadowDomComponent: Uses ViewEncapsulation.ShadowDom for true style isolation. NoneComponent: Uses ViewEncapsulation.None, allowing its styles to affect other components.

  • EmulatedComponent: Uses ViewEncapsulation.Emulated to ensure its styles don't leak out.
  • ShadowDomComponent: Uses ViewEncapsulation.ShadowDom for true style isolation.
  • NoneComponent: Uses ViewEncapsulation.None, allowing its styles to affect other components.