Angular Interview Questions For 5 Years Of Experience

Pravin M
62 min readAug 20, 2023
Angular Interview Questions For Experienced

Source:- Angular Interview Questions For 5 Years Of Experience

For more questions and answers visit our website at Frontend Interview Questions

  1. What are custom directives in Angular and how to create them ?

Custom directives are a feature in Angular that allow developers to extend the functionality of HTML by creating their own custom HTML elements or attributes. With custom directives, developers can define their own behavior, such as adding event listeners, modifying the DOM, or manipulating data.

To create a custom directive in Angular, follow these steps:

  1. Create a new directive using the @Directive decorator. The decorator specifies the selector for the directive and any inputs, outputs, and other options.
  2. Define the directive class, which contains the logic for the directive. The class should implement the OnInit and OnDestroy interfaces to handle the initialization and destruction of the directive.
  3. Add the directive to the declarations array in the module that will use it. This tells Angular that the directive should be available for use in that module.

Here is an example of a simple custom directive that changes the background color of an element:


import { Directive, ElementRef, Renderer2 } from '@angular/core';

@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) { }

ngOnInit() {
this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow');
}

ngOnDestroy() {
this.renderer.removeStyle(this.el.nativeElement, 'background-color');
}
}

In this example, the HighlightDirective sets the background color of an element to yellow when it is initialized, and removes the background color when it is destroyed. The ElementRef and Renderer2 classes are used to access and manipulate the element in the DOM. To use this directive in a template, simply add the appHighlight attribute to an element:


<p apphighlight>
This text will have a new yellow background.
</p>

When the template is rendered, the HighlightDirective will be applied to the element, changing its background color to yellow.

2. What are the security features in angular ?

Angular provides several security features and best practices to help developers build secure web applications. Here are some of the key security features in Angular, along with examples:

  1. Template Sanitization:

Angular automatically sanitizes user-provided inputs in templates to prevent Cross-Site Scripting (XSS) attacks. For example, consider the following template:


{{ user.name }}

If the `user.name` property contains potentially harmful HTML code, Angular automatically sanitizes it and renders it as plain text, preventing any script execution.

  • >We have to add code to sanitize untrusted values, The security contexts are HTML (binding inner HTML), style (CSS), attributes (binding values), and resources (referring files). We should covert the untrusted values provided by users into trusted values with DomSanitizer

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { DomSanitizer } from '@angular/platform-browser';

@Injectable()
export class SecurityService {
constructor(private sanitizer: DomSanitizer) {
}
getSafeHtml(html: string) {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}

The following methods are used for marking a value as trusted depending on the value type:

bypassSecurityTrustScript
bypassSecurityTrustStyle
bypassSecurityTrustUrl
bypassSecurityTrustResourceUrl

2. Cross-Site Scripting (XSS) Protection:

Angular automatically escapes interpolated values and data bindings by default to prevent XSS attacks. For example, consider the following template:


{{ user.bio }}

If the `user.bio` property contains a script tag or any other HTML code, Angular escapes the characters and renders it as plain text, preventing script execution.

3. Content Security Policy (CSP) Support:

Angular allows you to enforce a strict Content Security Policy for your application. This helps protect against XSS attacks by defining the sources from which the application can load resources. For example, you can configure a CSP in the HTML header as follows:


<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted-cdn.com;">

This example restricts the loading of scripts to the same origin and a trusted CDN.

4. HTTP Interceptors:

Angular’s HttpClient module provides interceptors that allow you to modify HTTP requests and responses. You can use interceptors to implement security-related features, such as adding authentication headers or handling CSRF tokens. For example, you can create an interceptor to add an authentication token to every outgoing request:


import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest, next: HttpHandler): Observable> {
const token = 'your-auth-token';
const authReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next.handle(authReq);
}
}

This interceptor adds an `Authorization` header with a bearer token to each outgoing HTTP request.

5. Authentication and Authorization:

Angular provides a flexible framework for implementing authentication and authorization mechanisms. You can leverage features like route guards, authentication services, and token-based authentication (e.g., JWT) to secure routes and control access to protected resources. Here’s an example of a route guard that restricts access to a specific route:


import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}

canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable | Promise | boolean | UrlTree {
// Check if user is authenticated, e.g., by verifying the presence of a valid token
const isAuthenticated = ...;

if (isAuthenticated) {
return true;
} else {
// Redirect to login page or show access denied message
this.router.navigate(['/login']);
return false;
}
}
}

This guard checks if the user is authenticated and allows or denies access to the protected route accordingly.

6. Avoid risky Angular APIs

Avoid Angular APIs marked in the documentation as “Security Risk.” The most common risky API we use is ElementRef. It permits direct access to the DOM and can make your application more vulnerable to XSS attacks. Review any use of ElementRef in your code carefully. Use this API as a last resort when direct access to the DOM is needed. Use templating and data binding provided by Angular, instead. Alternatively, you can take a look at Renderer2, which provides an API that can safely be used even when direct access to native elements is not supported. These are just a few examples of the security features provided by Angular. It’s important to implement additional security measures based on your application’s requirements, such as input validation, secure communication protocols (HTTPS), proper error handling, and regular security updates for dependencies and libraries used in the application.

3. What is storybook in Angular ?

Please checkout the detailed explaination here

4. What are the design patterns in Angular ?

In Angular, which is a popular JavaScript framework for building web applications, several design patterns are commonly used to structure and organize code. These design patterns help developers create maintainable, scalable, and modular applications. Here are some of the design patterns frequently utilized in Angular:

  1. Singleton Pattern: Angular services are often implemented using the Singleton pattern. A service is instantiated once and shared across multiple components, allowing them to communicate and share data.

To implement the Singleton pattern in Angular, you can follow these steps:

a. Create a service using the Angular CLI:


ng generate service MySingletonService

b. There are two ways to create a single service in angular that is by using

-> providedIn property

-> NgModule providers arrays

c. Open the generated service file (`my-singleton-service.service.ts`) and modify it as follows:


import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class MySingletonService {
// Your service implementation goes here
}

d. The `providedIn: ‘root’` property in the `@Injectable` decorator is key to implementing the Singleton pattern in Angular. This tells Angular to provide the service at the root level, making it accessible throughout the application.

e. You can now use the `MySingletonService` in your components by injecting it into their constructors:


import { Component } from '@angular/core';
import { MySingletonService } from './my-singleton-service.service';

@Component({
selector: 'app-my-component',
template: '...',
})
export class MyComponent {
constructor(private mySingletonService: MySingletonService) {
// Access the shared service instance here
}
}

By injecting `MySingletonService` into multiple components, you will be accessing the same instance of the service across the application, ensuring data consistency and sharing. It’s important to note that Angular itself manages the lifecycle of the singleton service. It creates and maintains a single instance of the service and shares it among components that request it. In the case of NgModule providers array, a singleton service is created by passing the service as a value to the providers array and if the NgModule is root app module then the service will be available throughout the application as a singleton service.


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MySingletonService } from './my-singleton-service.service';
import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [MySingletonService], // Add the service here
bootstrap: [AppComponent]
})
export class AppModule { }

That’s how you can implement the Singleton pattern in Angular using a service. This allows you to share data, maintain state, and provide centralized functionality throughout your application.

2. Dependency Injection (DI) Pattern: Angular utilizes the DI pattern to manage the dependencies between components and services. With DI, the required dependencies are provided to a component or service through constructor injection or property injection, promoting loose coupling and testability.


// Component using DI
constructor(private productService: ProductService) {
// Use the productService
}

3. Observer Pattern: Angular leverages the Observer pattern through the EventEmitter class and the RxJS library. Components can emit events using EventEmitters, and other components can subscribe to these events to react accordingly.


// Component emitting an event
@Output() productSelected = new EventEmitter();

selectProduct(product: Product) {
this.productSelected.emit(product);
}

// Component subscribing to an event

4. Strategy Pattern: The Strategy pattern enables you to dynamically select and switch between different strategies at runtime based on specific conditions or requirements. By encapsulating these behaviors in separate classes, components can switch between strategies based on specific conditions.

Here’s an example of implementing the Strategy pattern in Angular:

a. Define an interface that represents the common behavior of the strategies. Let’s assume we have a payment processing scenario:


// payment-strategy.interface.ts
export interface PaymentStrategy {
processPayment(amount: number): void;
}

b. Implement multiple strategies by creating separate classes that implement the `PaymentStrategy` interface. Each class will provide its own implementation of the `processPayment` method:


// credit-card-strategy.ts
export class CreditCardStrategy implements PaymentStrategy {
processPayment(amount: number): void {
console.log(`Processing credit card payment of $${amount}`);
// Perform credit card payment processing logic here
}
}

// paypal-strategy.ts
export class PaypalStrategy implements PaymentStrategy {
processPayment(amount: number): void {
console.log(`Processing PayPal payment of $${amount}`);
// Perform PayPal payment processing logic here
}
}

c. Create a context class that will use the strategies and provide a method to set the active strategy:


// payment-context.ts
import { PaymentStrategy } from './payment-strategy.interface';

export class PaymentContext {
private strategy: PaymentStrategy;

setStrategy(strategy: PaymentStrategy): void {
this.strategy = strategy;
}

processPayment(amount: number): void {
this.strategy.processPayment(amount);
}
}

d. Now, you can utilize the strategies in your Angular components or services. For example:


import { Component } from '@angular/core';
import { PaymentContext } from './payment-context';
import { CreditCardStrategy } from './credit-card-strategy';
import { PaypalStrategy } from './paypal-strategy';

@Component({
selector: 'app-payment-component',
template: '...',
})
export class PaymentComponent {
constructor(private paymentContext: PaymentContext) {}

processCreditCardPayment(amount: number): void {
this.paymentContext.setStrategy(new CreditCardStrategy());
this.paymentContext.processPayment(amount);
}

processPaypalPayment(amount: number): void {
this.paymentContext.setStrategy(new PaypalStrategy());
this.paymentContext.processPayment(amount);
}
}

e. In this example, the `PaymentComponent` uses the `PaymentContext` to switch between different payment strategies (`CreditCardStrategy` and `PaypalStrategy`) based on user actions or conditions. By setting the active strategy through `setStrategy`, you can dynamically change the behavior of the payment processing logic in `processPayment`. This implementation allows for easy extensibility, as you can add new strategies by implementing the `PaymentStrategy` interface and use them interchangeably within the `PaymentComponent` or any other component that requires payment processing functionality. The Strategy pattern provides flexibility and maintainability by separating the implementation of different algorithms or behaviors from the client code, allowing you to change or extend strategies without modifying existing code.

5. Decorator Pattern: Angular decorators, such as @Component and @Injectable, are based on the Decorator pattern. Decorators provide a way to enhance or modify the behavior of classes or class members without directly modifying the underlying code.

a. Create a base component that represents the core functionality:


import { Component } from '@angular/core';

@Component({
selector: 'app-base-component',
template: 'Base Component',
})
export class BaseComponent {}

b. Create a decorator component that extends the base component:


import { Component, ViewChild } from '@angular/core';
import { BaseComponent } from './base-component';

@Component({
selector: 'app-decorator',
template: `
<div>
<p>This is the decorator component</p>
<ng-content></ng-content>
</div>
`,
})
export class DecoratorComponent extends BaseComponent {}

In this example, the `DecoratorComponent` is a child component that extends the functionality of the `BaseComponent`. It wraps the `BaseComponent` within itself and adds extra content using `<ng-content>`. This allows you to inject additional behavior or template content around the base component.

c. Use the decorator component in your application:


import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<app-decorator>
<app-base-component></app-base-component>
</app-decorator>
`,
})
export class AppComponent {}

In the `AppComponent` template, the `BaseComponent` is wrapped within the `DecoratorComponent` using its selector `<app-decorator>`. You can inject other components, templates, or HTML content within the `DecoratorComponent` to extend or modify the behavior of the `BaseComponent`. By using the Decorator pattern in Angular, you can dynamically extend or modify the functionality of existing components by wrapping them within decorator components. This approach provides flexibility, code reusability, and maintainability, as you can reuse the base components while adding specific behavior or content as needed.

6. Facade Pattern:The Facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem, making it easier to use and understand. In Angular, you can apply the Facade pattern to create a simplified API or service that encapsulates the complexity of interacting with multiple components, services, or modules.

Here’s an example of implementing the Facade pattern in Angular:

a. Identify a complex subsystem or set of related components/services that you want to simplify for client usage.

b. Create a Facade service that encapsulates the interactions with the complex subsystem. The Facade service will provide a simplified interface for clients to access the subsystem’s functionality.


import { Injectable } from '@angular/core';
import { ComplexServiceA } from './complex-service-a';
import { ComplexServiceB } from './complex-service-b';

@Injectable()
export class FacadeService {
constructor(private serviceA: ComplexServiceA, private serviceB: ComplexServiceB) {}

// Provide simplified methods that internally call the appropriate complex subsystem methods
performOperation(): void {
this.serviceA.complexOperationA();
this.serviceB.complexOperationB();
}
}

c. Implement the complex subsystem components/services that the Facade service interacts with. These components/services handle the actual complex logic.


@Injectable()
export class ComplexServiceA {
complexOperationA(): void {
// Complex logic of service A
console.log('Performing complex operation A');
}
}

@Injectable()
export class ComplexServiceB {
complexOperationB(): void {
// Complex logic of service B
console.log('Performing complex operation B');
}
}

d. Use the Facade service in your components to simplify the usage of the complex subsystem:


import { Component } from '@angular/core';
import { FacadeService } from './facade.service';

@Component({
selector: 'app-client-component',
template: '...',
})
export class ClientComponent {
constructor(private facadeService: FacadeService) {}

performFacadeOperation(): void {
this.facadeService.performOperation();
}
}

e. In this example, the `ClientComponent` utilizes the `FacadeService` to perform complex operations without needing to interact directly with the complex subsystem (`ComplexServiceA` and `ComplexServiceB`). The `FacadeService` encapsulates the complexity and provides a simplified interface for the client component to interact with. By using the Facade pattern in Angular, you can simplify the usage of complex subsystems, hide their implementation details, and provide a straightforward and easy-to-use interface for clients. This promotes code maintainability, readability, and modularity by abstracting the complexity of interacting with multiple components or services behind a single facade.

7. Composite Pattern: The Composite Design Pattern is a structual design pattern that is used to compose objects into a tree-like structure. Components can be composed of other components, forming a tree-like structure. This pattern enables the creation of reusable and hierarchical UI components. In Angular, you can apply the Composite pattern to represent hierarchical relationships between components or services.

Here’s an example of implementing the Composite pattern in Angular:

a. Create an abstract class or interface that represents the common behavior for both individual objects and groups:


// component.interface.ts
export interface ComponentInterface {
operation(): void;
}

b. Implement the abstract class or interface for individual objects:


// leaf.component.ts
import { ComponentInterface } from './component.interface';

export class LeafComponent implements ComponentInterface {
operation(): void {
console.log('Performing operation on a leaf component.');
}
}

c. Implement the abstract class or interface for the composite object, which can contain both individual objects and other composite objects:


// composite.component.ts
import { ComponentInterface } from './component.interface';

export class CompositeComponent implements ComponentInterface {
private children: Component[] = [];

add(component: ComponentInterface): void {
this.children.push(component);
}

remove(component: ComponentInterface): void {
const index = this.children.indexOf(component);
if (index > -1) {
this.children.splice(index, 1);
}
}

operation(): void {
console.log('Performing operation on the composite component.');
for (const child of this.children) {
child.operation();
}
}
}

d. Use the composite object to create a tree-like structure of components:


import { ComponentInterface } from './component.interface';
import { LeafComponent } from './leaf.component';
import { CompositeComponent } from './composite.component';

// Create leaf components
const leaf1: ComponentInterface = new LeafComponent();
const leaf2: ComponentInterface = new LeafComponent();

// Create composite component
const composite: ComponentInterface = new CompositeComponent();
composite.add(leaf1);
composite.add(leaf2);

// Create another composite component
const composite2: ComponentInterface = new CompositeComponent();
composite2.add(composite);
composite2.add(leaf1);

// Perform operation on the composite structure
composite2.operation();

e. In this example, we create a tree-like structure using the Composite pattern. The `CompositeComponent` can contain both individual `LeafComponent` objects and other `CompositeComponent` objects. Calling the `operation()` method on the top-level `CompositeComponent` will recursively invoke the operation on all its children, whether they are leaf components or other composite components. By using the Composite pattern in Angular, you can represent complex hierarchical relationships between components or services in a uniform manner. It allows you to treat individual objects and groups of objects in a consistent way, simplifying the code and enabling recursive operations on the composite structure.

8.Factory Pattern: The Factory pattern is a creational design pattern that provides an interface for creating objects without specifying the exact class of the object that will be created. In Angular, you can apply the Factory pattern to encapsulate object creation logic and provide a centralized place for creating instances of different classes.

Here’s an example of implementing the Factory pattern in Angular:

a. Define an abstract class or interface that represents the common behavior of the objects you want to create:

// product.interface.ts
export interface Product {
operation(): void;
}

b. Implement multiple classes that conform to the `Product` interface:


// product-a.ts
export class ProductA implements Product {
operation(): void {
console.log('Product A operation.');
}
}

// product-b.ts
export class ProductB implements Product {
operation(): void {
console.log('Product B operation.');
}
}

c. Create a factory class that encapsulates the object creation logic:


// product-factory.ts
import { Product } from './product.interface';
import { ProductA } from './product-a';
import { ProductB } from './product-b';

export class ProductFactory {
createProduct(type: string): Product {
if (type === 'A') {
return new ProductA();
} else if (type === 'B') {
return new ProductB();
}

throw new Error('Invalid product type');
}
}

d. Use the factory class to create instances of the desired products:


import { Component } from '@angular/core';
import { ProductFactory } from './product-factory';
import { Product } from './product.interface';

@Component({
selector: 'app-example',
template: '...',
})
export class ExampleComponent {
constructor(private productFactory: ProductFactory) {}

createProduct(type: string): void {
const product: Product = this.productFactory.createProduct(type);
product.operation();
}
}

e. In this example, the `ExampleComponent` uses the `ProductFactory` to create instances of different products based on the provided type. By calling the `createProduct` method with the desired type (‘A’ or ‘B’), it receives an instance of the corresponding product class and can invoke its `operation()` method. Using the Factory pattern in Angular provides a centralized place for creating objects and decouples the client code from the concrete classes. It allows for flexible object creation and enables easy extensibility by adding new product classes and updating the factory logic accordingly. These are some of the design patterns commonly used in Angular. However, it’s worth noting that Angular itself follows the MVC (Model-View-Controller) architectural pattern, where components serve as the controllers, templates represent views, and services act as models.

5. What are interceptors in Angular ?

In Angular, interceptors are classes that can be used to intercept and manipulate HTTP requests and responses. They provide a way to modify or handle HTTP requests globally before they are sent to the server or to modify the HTTP responses before they are delivered to the calling code. Interceptors are useful for tasks like adding authentication headers, logging, error handling, caching, and more. To create an interceptor in Angular, you need to implement the HttpInterceptor interface and define the logic for intercepting requests and responses.

Here’s an example of an HTTP interceptor that adds an authorization header to outgoing requests:

  1. Create the interceptor class:

// auth-interceptor.ts
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
} from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(
request: HttpRequest,
next: HttpHandler
): Observable> {
// Add authorization header to the request
const authToken = 'your_auth_token_here';
const authRequest = request.clone({
setHeaders: { Authorization: `Bearer ${authToken}` },
});

// Pass the modified request to the next interceptor or the HTTP handler
return next.handle(authRequest);
}
}

2. Register the interceptor:

To use the interceptor, you need to provide it in the AppModule or the module where your HTTP requests are made.


// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth-interceptor';

@NgModule({
imports: [BrowserModule, HttpClientModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
],
})
export class AppModule {}

In this example, we register the AuthInterceptor as a provider for the HTTP_INTERCEPTORS multi-provider token. The `multi: true` option indicates that this interceptor can be one of many, as multiple interceptors can be provided.

Now, whenever an HTTP request is made in your application, the AuthInterceptor will intercept the request, add the authorization header, and pass it on to the next interceptor or the actual HTTP handler. This way, you can centralize common HTTP request-related tasks and keep your code clean and maintainable.

6. How to create a custom decorator in Angular ?

Decorators are a design pattern that is used to separate modification or decoration of a class without modifying the original source code. In Angular, decorators are functions that allow a service, directive or filter to be modified prior to its usage.

Create decorator :



function log(target,name,descriptor) {
const original=descriptor.value;
descriptor.value=function(...args) {
console.log('this function is hacked')
const result=original.apply(this,args)
console.log("the result of the function is ", result);
return result;
}
original();
return descriptor;
}

Usage of decorator :


@log
sum(a,b) {
return a+b;
}
//function overridden by decorator called
sum(2,3)

//output will be
this function is hacked
the result of the function is 5

7. How to make multiple http calls in parallel in Angular ?

This can be achieved by using forkJoin, this operator takes an array of observables and waits for all the source observables to complete. Once they all complete, it emits an array of the last emitted values from each observable.

Example:


import { forkJoin, of, throwError } from 'rxjs';

const observables = [
of(1,2,3).pipe(delay(500)),
from([10,11,12])
]

const $forkJoin = forkJoin(observables);

$forkJoin.subscribe(data=>{
console.log('forkjoin data', data); // [3,12] as forkJoin will return last emitted values of each observable
})

In this example, `forkJoin` takes an array of observables, including two observables that emit ‘A’ and ‘B’ respectively after a delay, and an observable that throws an error after a delay. `forkJoin` waits for all the observables to complete, and once they complete, it emits an array of the last emitted values from each observable. However, if any of the observables in `forkJoin` throws an error, the error will be propagated to the error callback of the `subscribe` method.

8. What is a module in TypeScript, and how can you use it ?

In TypeScript, a module is a way to organize code into reusable, self-contained units of code that can be imported and exported between different parts of an application. Modules can contain classes, functions, interfaces, and other code, and can be either internal to a project or external libraries.

To use a module in TypeScript, you need to define it using the `export` keyword, which makes its members available to other parts of the application. You can then import the module using the `import` keyword, which allows you to use its members in your code.

Here is an example of how to define and use a module in TypeScript:


// myModule.ts
export function myFunction() {
// code here
}

export class MyClass {
// code here
}

In this example, we define a module called `myModule` that exports a function called `myFunction` and a class called `MyClass`. The `export` keyword makes these members available outside of the module.

To use the members of the `myModule` module in another file, you can import them using the `import` keyword:


// main.ts
import { myFunction, MyClass } from "./myModule";

myFunction();
const myInstance = new MyClass();

In this example, we import the `myFunction` function and `MyClass` class from the `myModule` module using destructuring. We can then call the `myFunction` function and create an instance of the `MyClass` class, both using the imported names.

There are different ways to import and export modules in TypeScript, such as importing all members using the `* as` syntax, importing default exports, or using aliases for imported members. It is important to understand the different syntaxes and their implications, depending on the size and complexity of the project.

Using modules can help you write more modular and maintainable code in TypeScript, by isolating functionality and reducing naming conflicts. However, it is important to use them judiciously and not to create too many small modules, which can increase the complexity of the codebase.

9. How to hit an API before the loading of the appComponent ?

The package @angular/router has the Resolve property for routes. So you can easily resolve data before rendering a route view. The following example implements a resolve() method that retrieves the data needed to activate the requested route.


@Injectable({ providedIn: 'root' })
export class HeroResolver implements Resolve {
constructor(private service: HeroService) {}

resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable|Promise|any {
return this.service.getHero(route.paramMap.get('id'));
}
}

Here, the defined resolve() function is provided as part of the Route object in the router configuration:


@NgModule({
imports: [
RouterModule.forRoot([
{
path: 'detail/:id',
component: HeroDetailComponent,
resolve: {
hero: HeroResolver
}
}
])
],
exports: [RouterModule]
})
export class AppRoutingModule {}

10. If there is a package(or say library) that is not available through NPM , then how would you use it in your angular application ?

For anything that doesn’t have an npm package, you’ll have to download the file and put it somewhere in your project — I’d recommend putting it in a directory called vendor or lib. The import statement can use a relative path to the module you want to use, so it should be straightforward, e.g. if you put your third-party module in vendor/some-lib.js, you would import it with:

Code e.g


// src/foo.js
import './../vendor/some-lib';
If you want to get fancy, you can use resolve.alias in your webpack config so that you never have to work out the relative path.

// webpack.config.js
const path = require('path');

// ...
resolve: {
alias: {
vendor: path.resolve(__dirname, 'vendor')
}
},

// src/foo.js
import 'vendor/some-lib';

11. How to optimize Angular application using OnPush change detection strategy ?

The `OnPush` change detection strategy in Angular is designed to optimize performance by reducing the number of change detection cycles. It is based on the concept of immutability and relies on input properties and explicit change detection triggering.

To optimize an Angular application using the `OnPush` strategy, follow these steps:

Let’s assume we have a parent component called `ParentComponent` and a child component called `ChildComponent`. The child component receives an array of items as an input property and displays them in a list. We’ll optimize this scenario using the `OnPush` strategy.

  1. Use the `OnPush` change detection strategy in components:

// parent.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'app-parent',
template: `
<app-child [items]="items"></app-child>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentComponent {
items: string[] = ['Item 1', 'Item 2', 'Item 3'];

addItem() {
this.items.push('New Item'); // Incorrect way to update the array
}
}

// child.component.ts
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'app-child',
template: `
<ul>
<li *ngfor="let item of items">{{ item }}</li>
</ul>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() items: string[];
}type

In the above code, both the `ParentComponent` and `ChildComponent` are using the `OnPush` change detection strategy.

2. Use immutable data for input properties:


// parent.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'app-parent',
template: `
<app-child [items]="items"></app-child>
<button (click)="addItem()">Add Item</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentComponent {
items: string[] = ['Item 1', 'Item 2', 'Item 3'];

addItem() {
this.items = [...this.items, 'New Item']; // Correct way to update the array using spread operator
}
}

In the `ParentComponent`, when adding a new item to the `items` array, we create a new array using the spread operator `[…this.items]` and then add the new item. This ensures that a new reference is created, which triggers change detection in the `ChildComponent` when the input property changes.

Conclusion :

By applying these optimization techniques, you can enhance the performance of your Angular application with the `OnPush` change detection strategy. Immutable data help minimize unnecessary change detection cycles, resulting in improved efficiency and responsiveness.

12. What are the types of forms available in Angular ? Explain with syntax.

In Angular, there are different types of forms available for handling user input and performing validation. The two main types of forms in Angular are Template-driven forms and Reactive forms.

  1. Template-driven Forms: — Template-driven forms are primarily defined within the HTML template of the component using Angular directives. — The form controls and validation rules are inferred from the template, reducing the need for explicit form control declaration in the component code. — Template-driven forms are suitable for simple forms with basic validation requirements.

Syntax:


<form #myform="ngForm" (ngsubmit)="onSubmit()">
<label for="name">Name:</label>
<input type="text" id="name" name="name" ngmodel="" required="">

<label for="email">Email:</label>
<input type="email" id="email" name="email" ngmodel="" required="" email="">

<button type="submit">Submit</button>
</form>

In the example above, `ngForm` is a directive that represents the entire form. Each input element uses the `ngModel` directive for two-way data binding and includes additional directives like `required` and `email` for validation.

2. Reactive Forms:

Reactive forms are created programmatically in the component class using TypeScript. — Form controls are explicitly defined in the component code, providing more control and flexibility. — Reactive forms are suitable for complex forms with dynamic validation requirements and advanced interactivity.

Syntax:


import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
selector: 'app-my-form',
template: `
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<label for="name">Name:</label>
<input type="text" id="name" formControlName="name">

<label for="email">Email:</label>
<input type="email" id="email" formControlName="email">

<button type="submit">Submit</button>
</form>
`,
})
export class MyFormComponent implements OnInit {
myForm: FormGroup;

ngOnInit() {
this.myForm = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email]),
});
}

onSubmit() {
if (this.myForm.valid) {
// Handle form submission
}
}
}

In the example above, `FormGroup` represents the entire form, while `FormControl` represents individual form controls. Validators are applied to each form control to define validation rules.

Conclusion:

These are the main types of forms available in Angular: Template-driven forms and Reactive forms. Depending on the complexity and requirements of your form, you can choose the appropriate form type for your Angular application.

13. How to improve Angular app performance ?

These are a few essential hacks that can help to significantly improve the Angular app performance :

  1. Using AoT Compilation.
  2. Using OnPush Change Detection Strategy.
  3. Using Pure Pipes.
  4. Unsubscribe from Observables
  5. Lazy Loading.
  6. Use trackBy option for For Loop.
  7. Usage of Web Workers.

14. What is AuthGuard in Angular ?

In Angular, Auth Guards are used to protect routes and determine whether a user is allowed to access a specific route or not based on their authentication status or user role. Auth Guards are implemented as services and are typically used in conjunction with Angular’s routing system.

Here’s an example to demonstrate how Auth Guards work in Angular:

  1. Create an Auth Guard service:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {

constructor(private router: Router) {}

canActivate(): boolean {
// Check the user's authentication status or role
const isAuthenticated = /* your authentication check */;
const userRole = /* get user role */;

if (isAuthenticated && userRole === 'admin') {
return true; // Allow access to the route
} else {
this.router.navigate(['/login']); // Redirect to login page or unauthorized page
return false; // Deny access to the route
}
}
}

2. Define routes in the app module:


import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home.component';
import { AdminComponent } from './admin.component';
import { LoginComponent } from './login.component';
import { AuthGuard } from './auth.guard';

const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

In this example, we have three routes: the home route, the admin route, and the login route. The admin route is protected by the `AuthGuard` by setting the `canActivate` property to `[AuthGuard]`.

3. Implement the protected component:


import { Component } from '@angular/core';

@Component({
selector: 'app-admin',
template: 'Admin Page'
})
export class AdminComponent { }

4. Use the Auth Guard in the template or component:


<!-- Example of using Auth Guard in a template -->
<button [routerlink]="['/admin']" *ngif="isAuthenticated">Go to Admin</button>

<!-- Example of using Auth Guard in a component -->
import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
selector: 'app-some-component',
template: `
<button (click)="goToAdmin()" *ngif="isAuthenticated">Go to Admin</button>
`
})
export class SomeComponent {
isAuthenticated: boolean;

constructor(private router: Router) {}

goToAdmin() {
if (this.isAuthenticated) {
this.router.navigate(['/admin']);
}
}
}

In both examples above, the Auth Guard is used to conditionally display or navigate to the admin route based on the user’s authentication status. If the user is authenticated and has the appropriate role (in this case, ‘admin’), they are granted access to the route. Otherwise, they are redirected to the login page or an unauthorized page.

Conclusion :

Auth Guards play a crucial role in securing routes and controlling access to different parts of an Angular application based on authentication and authorization rules. By implementing and utilizing Auth Guards, you can ensure that certain routes are protected and only accessible to authorized users.

15. Explain switch map , mergemap , forkjoin, combineLatest, concatMap, exhaustMap . what would happen if any of the requests fails in switch map , merge map or forkjoin ?

Let’s go through each operator and explain them along with examples. We’ll also discuss what happens if any of the requests fail when using `switchMap`, `mergeMap`, or `forkJoin`.

  1. switchMap: This operator is used to map each source value to an inner observable, and it only emits the values from the most recent inner observable. If a new source value arrives before the previous inner observable completes, it will switch to the new inner observable and unsubscribe from the previous one.

Example:


import { of, interval } from 'rxjs';
import { switchMap } from 'rxjs/operators';
const $switchMap = from([1,2,3,4]).pipe(switchMap(data=>{
return of(data).pipe(delay(500))
}));

$switchMap.subscribe(data=>{
console.log('switch map data', data); // 4 as switchMap cancels all previous observables when new observable is emitted
})

In this example, `sourceObservable` emits values every second. For each value emitted, `switchMap` creates an inner observable using `from` operator and emits the value from the source observable after a delay of one second. If a new value is emitted before the previous inner observable completes, it switches to the new inner observable and cancels the previous one. Therefore, only the most recent inner observable value will be emitted.

2. mergeMap: This operator maps each source value to an inner observable and merges the values from multiple inner observables into a single observable. It does not cancel or unsubscribe from any inner observables.

Example:


import { of } from 'rxjs';
import { mergeMap, delay } from 'rxjs/operators';

// Create an observable that emits three values
const sourceObservable = of(1, 2, 3);

// Use mergeMap to merge the values from the inner observables
const resultObservable = sourceObservable.pipe(
mergeMap((value) => {
// Create an inner observable that emits the value after a delay
return of(value).pipe(delay(1000));
})
);

// Subscribe to the result observable
resultObservable.subscribe((value) => {
console.log(value); // 1 2 3
});

In this example, `sourceObservable` emits three values: 1, 2, and 3. For each value emitted, `mergeMap` creates an inner observable using `of` operator and emits the value after a delay of one second. Since `mergeMap` does not cancel or switch between inner observables, all the values from each inner observable will be merged into a single observable and emitted in the order they complete.

3. forkJoin: This operator takes an array of observables and waits for all the source observables to complete. Once they all complete, it emits an array of the last emitted values from each observable.

Example:


import { forkJoin, of, throwError } from 'rxjs';

const observables = [
of(1,2,3).pipe(delay(500)),
from([10,11,12])
]

const $forkJoin = forkJoin(observables);

$forkJoin.subscribe(data=>{
console.log('forkjoin data', data); // [3,12] as forkJoin will return last emitted values of each observable
})

In this example, `forkJoin` takes an array of observables, including two observables that emit ‘A’ and ‘B’ respectively after a delay, and an observable that throws an error after a delay. `forkJoin` waits for all the observables to complete, and once they complete, it emits an array of the last emitted values from each observable. However, if any of the observables in `forkJoin` throws an error, the error will be propagated to the error callback of the `subscribe` method.

4. combineLatest: This operator combines the latest values from multiple observables into a single observable. It emits an array of the latest values whenever any of the source observables emit a new value.

Example:


import { combineLatest, interval } from 'rxjs';
const observables = [
of(1,2,3,4),
from([10,11,12])
]

const $combineLatest = combineLatest(observables);

$combineLatest.subscribe(data=> {
console.log('combineLatest data', data);
})
/* output */
// [4, 10]
// [4,11]
// [4,12]

If our observable is as shown below :


const observables = [
of(1,2,3,4).pipe(delay(500)),
from([10,11,12])
]

then output will be as follows

// [12,1]
// [12,2]
// [12,3]
// [12,4]

The console.log() output from the project function shows that the last emitted value from the first completed observable is used in all calculations. It is combined with each of the second observable values .Hence: if one observable emits values before the others do, then those values are lost.

5. concatMap: This operator maps each source value to an inner observable and concatenates the values from each inner observable sequentially. It waits for each inner observable to complete before moving on to the next one. Use concatMap over mergeMap when order is important for you.

Example:


import { of } from 'rxjs';
import { concatMap, delay } from 'rxjs/operators';

// Create an observable that emits three values
const sourceObservable = of(1, 2, 3);

// Use concatMap to concatenate the values from the inner observables
const resultObservable = sourceObservable.pipe(
concatMap((value) => {
// Create an inner observable that emits the value after a delay
return of(value).pipe(delay(1000));
})
);

// Subscribe to the result observable
resultObservable.subscribe((value) => {
console.log(value); // 1 2 3
});

In this example, `sourceObservable` emits three values: 1, 2, and 3. For each value emitted, `concatMap` creates an inner observable using `of` operator and emits the value after a delay of one second. It waits for each inner observable to complete before moving on to the next one. Therefore, the values from each inner observable will be emitted sequentially in the order they were mapped.

6. exhaustMap: The exhaustMap operator works by mapping each source value to an observable, and then subscribing to that observable. It ensures that only one inner observable is active at a time. If a new source value arrives while an inner observable is still active, the new value is ignored until the inner observable completes.

Here’s an example to demonstrate the usage of exhaustMap in Angular:


const $exhaustMap = from([1,2,3,4]).pipe(
exhaustMap(data=>{
return of(data).pipe(delay(500));
})
)

$exhaustMap.subscribe(data=> {
console.log('exhaustMap data', data); //1
})

Regarding what happens if any of the requests fail in `switchMap`, `mergeMap`, or `forkJoin`:

— switchMap: If any of the inner observables created by `switchMap` throws an error, the error will be propagated to the error callback of the `subscribe` method. Additionally, the subscription to the previous inner observable will be canceled, and `switchMap` will switch to the new inner observable.

— mergeMap: If any of the inner observables created by `mergeMap` throws an error, the error will be propagated to the error callback of the `subscribe` method. However, the error in one inner observable will not affect the other inner observables. `mergeMap` will continue to merge values from other inner observables.

-forkJoin: If any of the observables passed to `forkJoin` throws an error, the error will be propagated to the error callback of the `subscribe` method. In this case, `forkJoin` will not emit any result value. If you need to handle individual errors of each observable in `forkJoin`, you can use the `catchError` operator within each observable before passing them to `forkJoin`.

It’s important to note that error handling strategies and behavior may vary based on your specific use case and how you handle errors within your code.

16. What is dependency injection ?

In software enginering — dependency injection is the process of creating an instance (object) of a class A and providing this instance to class B so that the functionalities of class A can be use in class B.

In context of Angular — dependency injection is the process of creating an instance (object) of a Service and providing this instance to a component so that the functionalities of service can be use in the component. One of the ways to implement dependency injection is through the constructor method of the component.

17. How do you handle errors in RxJS observables ?

RxJS provides several operators for handling errors in Observables. The two main operators for error handling are `catchError` and `retry`.

  1. catchError: The `catchError` operator is used to catch errors that may occur in an Observable and handle them in a graceful way. It takes a function as an argument that returns another Observable or throws an error. If the function returns an Observable, the source Observable will be replaced with the returned Observable. If the function throws an error, the error will be propagated to the subscriber.

Here is an example:


import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';

of(1, 2, 3).pipe(
map(num => {
if (num === 2) {
throw new Error('Oops!');
}
return num;
}),
catchError(err => {
console.error(err.message);
return of(4, 5, 6);
})
).subscribe(
num => console.log(num),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `map` operator throws an error when it encounters the number 2. The `catchError` operator catches the error and logs the error message to the console. It then replaces the source Observable with a new Observable that emits the numbers 4, 5, and 6.

2. retry: The `retry` operator is used to automatically retry an Observable when it encounters an error. It takes an optional argument that specifies the maximum number of retries.

Here is an example:



import { of } from 'rxjs';
import { map, retry } from 'rxjs/operators';

of(1, 2, 3).pipe(
map(num => {
if (num === 2) {
throw new Error('Oops!');
}
return num;
}),
retry(2)
).subscribe(
num => console.log(num),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `map` operator throws an error when it encounters the number 2. The `retry` operator retries the Observable up to 2 times before propagating the error to the subscriber.

18. How do you implement backpressure in RxJS ?

Backpressure is a mechanism used in reactive programming to handle situations where an Observable is emitting data at a faster rate than it can be consumed. This can lead to issues such as high memory usage, slow processing, and even crashes. RxJS provides several operators for implementing backpressure, including `buffer`, `throttle`, `debounce`, `sample`, and `switchMap`.

  1. buffer: The `buffer` operator collects emitted values from the source Observable into an array and emits the array when it reaches a specified size. It can be used to temporarily store emitted values until they can be processed.

Here is an example:


import { interval } from 'rxjs';
import { bufferTime } from 'rxjs/operators';

interval(100).pipe(
bufferTime(1000)
).subscribe(
values => console.log(values),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `interval` Observable emits a value every 100 milliseconds. The `bufferTime` operator collects the emitted values into an array and emits the array every 1000 milliseconds.

2. throttle: The `throttle` operator throttles the emissions of the source Observable by discarding emissions that occur within a specified time window. It can be used to limit the rate of emissions from the source Observable.

Here is an example:


import { interval } from 'rxjs';
import { throttleTime } from 'rxjs/operators';

interval(100).pipe(
throttleTime(1000)
).subscribe(
num => console.log(num),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `interval` Observable emits a value every 100 milliseconds. The `throttleTime` operator discards emissions that occur within 1000 milliseconds of the previous emission.

3. debounce: The `debounce` operator delays emissions from the source Observable until a specified time has elapsed since the last emission. It can be used to filter out rapid emissions and emit only the last value.

Here is an example:


import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

fromEvent(document, 'keyup').pipe(
debounceTime(1000)
).subscribe(
event => console.log(event.target.value),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `fromEvent` Observable emits a value every time a key is released on the document. The `debounceTime` operator delays emissions until 1000 milliseconds have elapsed since the last emission.

4. sample: The `sample` operator emits the most recent value from the source Observable at a specified time interval. It can be used to emit the most recent value at a regular interval, regardless of how many values are emitted.

Here is an example:


import { interval } from 'rxjs';
import { sampleTime } from 'rxjs/operators';

interval(100).pipe(
sampleTime(1000)
).subscribe(
num => console.log(num),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `interval` Observable emits a value every 100 milliseconds. The `sampleTime` operator emits the most recent value at 1000 millisecond intervals.

5. switchMap: The `switchMap` operator can be used to limit the number of concurrent emissions from the source Observable.

Here’s an example of using `switchMap` to implement backpressure:


import { from, interval } from 'rxjs';
import { switchMap } from 'rxjs/operators';

// An Observable that emits a value every 100ms
const source$ = interval(100);

// An Observable that processes values
const processValue = value => {
return from(new Promise(resolve => {
// Simulate processing time
setTimeout(() => {
console.log(`Processed value: ${value}`);
resolve();
}, 1000);
}));
};

// Use switchMap to limit the number of concurrent emissions
const limitedSource$ = source$.pipe(
switchMap(value => processValue(value), 2) // Only allow 2 concurrent emissions
);

limitedSource$.subscribe(
value => console.log(`Received value: ${value}`),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `source$` Observable emits a value every 100 milliseconds. The `processValue` function simulates processing time by returning a Promise that resolves after 1 second. The `switchMap` operator limits the number of concurrent emissions to 2, so only 2 values will be processed at a time. The `limitedSource$` Observable is subscribed to and emits the processed values.

Using `switchMap` in this way ensures that the processing of values is limited to a specific number at a time, preventing the system from being overwhelmed with too many values to process at once.

19. Can you explain what is the purpose of using schedulers in RxJS, and give an example of a common scheduler ?

In RxJS, a scheduler is an object that provides a way to control the timing of when events are emitted by observables. Schedulers can be used to schedule tasks to be executed at a specific time, delay the execution of tasks, or specify on which thread the tasks should be executed. The purpose of using schedulers is to provide developers with more fine-grained control over the timing and execution of observables.

One common scheduler in RxJS is the `observeOn()` operator. The `observeOn()` operator is used to specify the scheduler on which an observable should emit its values.

Here’s an example:


import { from } from 'rxjs';
import { observeOn } from 'rxjs/operators';
import { asyncScheduler } from 'rxjs';

const source$ = from([1, 2, 3]);

const async$ = source$.pipe(
observeOn(asyncScheduler) // Emit values on the async scheduler
);

async$.subscribe(
value => console.log(value), // Output: 1, 2, 3
err => console.error(err),
() => console.log('Complete')
);

In this example, the `from()` function is used to create an observable that emits the values 1, 2, and 3. The `observeOn()` operator is then used to specify that the observable should emit its values on the async scheduler, which will cause the values to be emitted asynchronously. The `asyncScheduler` is a common scheduler in RxJS that schedules tasks to be executed asynchronously using `setTimeout()`.

Schedulers can also be used to delay the execution of tasks, control the order in which tasks are executed, or specify on which thread the tasks should be executed. Some common schedulers in RxJS include `async`, `queue`, `animationFrame` and `immediate` among others.

20. What is the difference between a pipeable operator and a patch operator in RxJS ?

In RxJS, there are two ways to use operators — as pipeable operators or patch operators. The key difference between them is in how they are imported and used in code.

Pipeable operators are the recommended way to use operators in RxJS since version 5.5. Pipeable operators are imported as standalone functions and are then used in a pipeline by chaining them together with the `pipe()` function. Pipeable operators are pure functions that take an observable as their input and return a new observable as their output, allowing multiple operators to be composed together to form a pipeline.

Here’s an example of using pipeable operators to transform an observable:


import { of } from 'rxjs';
import { map, filter } from 'rxjs/operators';

const source$ = of(1, 2, 3, 4, 5);

const filtered$ = source$.pipe(
filter(value => value % 2 === 0),
map(value => value * 2)
);

filtered$.subscribe(
value => console.log(value), // Output: 4, 8
err => console.error(err),
() => console.log('Complete')
);

In this example, the `filter()` and `map()` operators are imported as standalone functions and then used in a pipeline with the `pipe()` function to create a new observable. The `filter()` operator is used to only allow even values to pass through, and the `map()` operator is used to double the remaining values.

Patch operators, on the other hand, were the original way to use operators in RxJS prior to version 5.5. Patch operators are imported as methods on the `Observable` class and are then used by calling them directly on an observable. Patch operators modify the behavior of the observable instance they are called on, and cannot be composed together in the same way as pipeable operators.

Here’s an example of using patch operators to transform an observable:


import { of } from 'rxjs';

const source$ = of(1, 2, 3, 4, 5);

const filtered$ = source$
.filter(value => value % 2 === 0)
.map(value => value * 2);

filtered$.subscribe(
value => console.log(value), // Output: 4, 8
err => console.error(err),
() => console.log('Complete')
);

In this example, the `filter()` and `map()` operators are called directly on the `source$` observable instance, which modifies its behavior to only allow even values to pass through and double the remaining values. Patch operators can still be used in RxJS, but pipeable operators are recommended for their increased composability and modularity.

21. What is the difference between AOT and JIT ?

AOT (Ahead-of-Time) and JIT (Just-in-Time) are two compilation methods used in Angular. Here’s a comparison of the two:

JIT (Just-in-Time):

  1. Compilation: JIT compilation happens at runtime in the user’s browser. The Angular compiler runs in the browser and compiles the application’s templates and components into JavaScript during the application’s bootstrap process.
  2. Development mode: JIT is primarily used during development as it allows for rapid iterations and immediate feedback. It supports features like hot module replacement, which speeds up the development process.
  3. Performance: JIT compilation can impact the initial load time of the application because the compilation process happens at runtime. The browser needs to download the Angular compiler and perform the compilation process, which can lead to a slower startup time.
  4. Debugging: JIT allows for better debugging experience as the browser can map the compiled code to the original TypeScript source files, enabling developers to debug directly in the browser’s developer tools.

AOT (Ahead-of-Time):

  1. Compilation: AOT compilation occurs before the application is deployed. The Angular compiler runs on the developer’s machine during the build process and generates pre-compiled JavaScript code. The compiled code includes templates and components converted into efficient JavaScript code.
  2. Production mode: AOT is primarily used in production deployments to optimize the performance and load time of the application. It eliminates the need for the Angular compiler in the browser, resulting in faster startup times and smaller bundle sizes.
  3. Performance: AOT significantly improves the initial load time of the application. The browser downloads pre-compiled JavaScript code, reducing the amount of work needed to be done at runtime.
  4. Security: AOT provides a level of security by pre-compiling the templates and removing the Angular compiler from the client-side code. This mitigates the risk of template injection attacks.
  5. Smaller bundle size: AOT allows for tree shaking, a process that eliminates unused code during the compilation phase. This leads to smaller bundle sizes and reduces the overall download size for users.
  6. Limited dynamic behavior: AOT introduces some limitations on dynamic behaviors, such as dynamic template generation or dynamic component loading, as the templates and components are pre-compiled during the build process.

In summary, JIT compilation is used during development, provides a better debugging experience, but can impact the initial load time. On the other hand, AOT compilation is primarily used in production deployments, improves performance and security, and reduces bundle sizes, but has limitations on dynamic behaviors.

22. How do you use the retry() operator in RxJS, and what is its purpose ?

The `retry()` operator is used in RxJS to resubscribe to an observable if an error occurs. The operator will automatically resubscribe to the source observable, potentially with a delay or other customization options, and continue emitting values to its subscribers. The `retry()` operator can be useful in scenarios where the observable may fail due to intermittent network errors or other issues, allowing the application to recover from these errors and continue operating.

Here’s an example of how to use the `retry()` operator:


import { of } from 'rxjs';
import { map, mergeMap, retry } from 'rxjs/operators';

const source$ = of('http://my-api.com/data');

const data$ = source$.pipe(
mergeMap(url => fetch(url)), // Assume fetch() returns a promise with the data
map(response => response.json()),
retry(3) // Retry up to 3 times if an error occurs
);

data$.subscribe(
data => console.log(data),
err => console.error(err),
() => console.log('Complete')
);

In this example, the `source$` observable emits a single value — a URL that points to an API endpoint. The `mergeMap` operator is used to call the `fetch()` function with the URL and return a promise that resolves with the response data. The `map` operator is then used to parse the response data as JSON.

The `retry()` operator is used after the `map()` operator to specify that the observable should be retried up to 3 times if an error occurs. If an error occurs during the execution of the observable, RxJS will automatically resubscribe to the source observable up to 3 times, allowing the application to potentially recover from network errors.

It’s worth noting that the `retry()` operator can be customized with additional options, such as a delay between retries or a predicate function that determines which errors should trigger a retry. These options can be useful for handling more complex scenarios.

23. What is Angular change detection and how does it work ?

Angular’s change detection is a mechanism that detects and propagates changes in the application’s data model to update the corresponding views. It ensures that the UI reflects the current state of the data. When there are changes in the application’s data, Angular’s change detection system automatically updates the affected components and their child components. Here’s how Angular’s change detection works:

  1. Initialization: When a component is created, Angular initializes its change detector. The change detector tracks the component’s properties and listens for changes.
  2. Change detection tree: Angular constructs a tree-like structure known as the change detection tree, which represents the component hierarchy. Each component has its own change detector, and child components are nested within their parent’s change detector.
  3. Detecting changes: Angular performs change detection by running a change detection cycle. This cycle is triggered by various events, such as user interactions, timers, or asynchronous operations. By default, Angular automatically triggers change detection for the entire application after these events.
  4. Change detection cycle: During a change detection cycle, Angular starts from the root component’s change detector and traverses the change detection tree in a top-down manner.
  5. Checking for changes: In each component, Angular checks the properties that are bound to the component’s template. It compares the current value of each property with its previous value.
  6. Updating the view: If Angular detects a change in a component’s property, it updates the corresponding view to reflect the new value. This includes updating the DOM, re-rendering the component’s template, and triggering any necessary reflows.
  7. Child component check: After updating the current component, Angular continues the change detection cycle by moving to the child components within the change detection tree. It recursively performs change detection on each child component.
  8. Binding propagation: If changes occur in a parent component, Angular propagates these changes to its child components. This ensures that any affected child components are also updated accordingly.
  9. Immutable data: Angular’s change detection relies on object references to detect changes. If the reference to an object remains the same, Angular assumes that the object has not changed. Therefore, when working with immutable data patterns, it’s important to ensure that new objects are created to represent changes.
  10. Performance optimizations: Angular’s change detection system includes several performance optimizations. It skips unnecessary change detection cycles for components that have not been affected by changes. Angular also supports the OnPush change detection strategy, which allows components to specify that they should only be checked for changes when their input properties change.

By efficiently detecting and propagating changes, Angular’s change detection system helps keep the application’s UI in sync with the underlying data model, ensuring a responsive and up-to-date user experience.

24. What is zone js in Angular ?

Zone.js is a JavaScript library used in Angular to provide execution context and hooks into asynchronous operations. It allows Angular to track and manage the execution of asynchronous tasks, such as event handling, timers, promises, and XHR requests. Zone.js enables Angular to perform change detection and update the UI when asynchronous operations complete.

Here’s an example to illustrate the use of Zone.js in Angular:


import { Component } from '@angular/core';

@Component({
selector: 'app-example',
template: `
<button (click)="simulateAsyncTask()">Simulate Async Task</button>
<p>Status: {{ status }}</p>
`
})
export class ExampleComponent {
status: string = 'Not started';

simulateAsyncTask() {
this.status = 'Processing...';

setTimeout(() => {
// Simulating an asynchronous task completion
this.status = 'Completed';
}, 2000);
}
}

In this Angular component, we have a button and a paragraph displaying the status of an asynchronous task. When the button is clicked, the `simulateAsyncTask()` method is called. Inside the method, we update the `status` property to indicate that the task is being processed. Then, we use the `setTimeout` function to simulate a delay of 2 seconds.

Behind the scenes, Zone.js intercepts the `setTimeout` call and hooks into the asynchronous operation. It allows Angular to track the execution of the task and ensures that change detection is triggered when the task completes. When the timeout expires, the callback function is executed, and the `status` property is updated to indicate that the task is completed. As a result, the UI is automatically updated to reflect the new status.

Conclusion :

Zone.js provides Angular with a way to seamlessly integrate asynchronous operations into the change detection mechanism, enabling efficient updating of the UI when asynchronous tasks finish. It simplifies the handling of asynchronous code and ensures that Angular remains aware of changes happening within the asynchronous context.

25. How to dynamically create form fields with FormArray in Angular ?

To dynamically create form fields with `FormArray` in Angular, you can follow these steps:

  1. Import the necessary modules and services: — Import `FormBuilder` and `FormGroup` from `@angular/forms`.
  2. Create the form group and form array in the component: — In the component class, create a form group using the `FormBuilder` and define a form array within it.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';

@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html',
})
export class DynamicFormComponent implements OnInit {
dynamicForm: FormGroup;

constructor(private formBuilder: FormBuilder) {}

ngOnInit() {
this.dynamicForm = this.formBuilder.group({
formArrayName: this.formBuilder.array([]),
});
}

get formArray(): FormArray {
return this.dynamicForm.get('formArrayName') as FormArray;
}

// Other methods for adding, removing, and accessing form array controls
}

3. Implement methods to add and remove form array controls:

— Implement methods to add and remove form array controls within the component.

— These methods should use the `FormArray` methods `push()` and `removeAt()` to add or remove form array controls.


//...

addFormControl() {
const control = this.formBuilder.control('', Validators.required);
this.formArray.push(control);
}

removeFormControl(index: number) {
this.formArray.removeAt(index);
}

//...

4. Generate form fields dynamically in the template:

— In the component’s template, use `*ngFor` to iterate over the form array controls and generate the corresponding form fields dynamically.


<form [formgroup]="dynamicForm" (ngsubmit)="onSubmit()">
<div formarrayname="formArrayName">
<div *ngfor="let control of formArray.controls; let i = index">
<input [formcontrolname]="i" type="text">
<button (click)="removeFormControl(i)">Remove</button>
</div>
</div>
<button (click)="addFormControl()">Add Field</button>
<button type="submit">Submit</button>
</form>

In the example above, the `formArray` is accessed using the `formArrayName` property, and `*ngFor` is used to iterate over the form array controls. Each control is rendered as an input field, and a “Remove” button is provided to remove the corresponding control.

5. Handle form submission:

— Implement the logic to handle form submission in the component, using the `FormGroup` instance to access the form values and perform any necessary operations.


//...

onSubmit() {
if (this.dynamicForm.valid) {
const formValues = this.dynamicForm.value;
// Handle form submission
}
}

//...

By following these steps, you can dynamically create form fields using `FormArray` in Angular. The form array allows you to add or remove form controls dynamically, and the form values can be accessed and processed as needed.

26. What is ngZone in Angular ?

In Angular, `NgZone` is a service provided by the Angular framework that helps manage and control the execution of asynchronous tasks and change detection. It is responsible for triggering change detection and updating the view when changes occur.

The primary purpose of `NgZone` is to handle and optimize the execution of code that runs outside of Angular’s zone, such as events from third-party libraries or asynchronous operations like timers, AJAX requests, or WebSockets.

By default, Angular runs in a zone called the “Angular zone.” When code executes within this zone, Angular’s change detection mechanism is triggered automatically, and the view is updated accordingly. However, when code runs outside of the Angular zone, Angular may not be aware of the changes, leading to potential issues with the application state and view synchronization.

`NgZone` provides a way to explicitly run code inside or outside of the Angular zone. It offers two methods for executing code: `run()` and `runOutsideAngular()`.

1. run() : The `run()` method executes the provided function inside the Angular zone. This ensures that any changes triggered by the function will be detected and updated in the view.

import { Component, NgZone } from '@angular/core';

@Component({
selector: 'app-example',
template: `
<button (click)="onClick()">Run Code Inside NgZone</button>
`,
})
export class ExampleComponent {
constructor(private ngZone: NgZone) {}
onClick() {
this.ngZone.run(() => {
// Code executed inside NgZone
// Angular change detection is triggered
});
}
}

In the above example, the `onClick()` method is wrapped inside the `run()` method of `NgZone`. When the button is clicked, the code inside the `run()` function is executed within the Angular zone, ensuring that any changes made are detected and updated in the view.

2. runOutsideAngular() : The `runOutsideAngular()` method allows you to execute code outside of the Angular zone. This is useful for running code that doesn’t require Angular’s change detection or when optimizing performance for tasks that don’t affect the UI.

import { Component, NgZone } from '@angular/core';

@Component({
selector: 'app-example',
template: `
<button (click)="onClick()">Run Code Outside NgZone</button>
`,
})
export class ExampleComponent {
constructor(private ngZone: NgZone) {}
onClick() {
this.ngZone.runOutsideAngular(() => {
// Code executed outside NgZone
// Angular change detection is not triggered
});
}
}

In the above example, the `onClick()` method runs the code inside the `runOutsideAngular()` method. This ensures that the code is executed outside of the Angular zone, preventing unnecessary change detection and view updates.

Conclusion :

Using `NgZone`, you can control and optimize the execution of code inside and outside the Angular zone, ensuring efficient change detection and synchronization between the application state and the view.

27. Can event triggered in parent cause change detection in child with OnPush strategy in Angular ?

No, events triggered in the parent component cannot directly cause change detection in a child component with the `OnPush` change detection strategy in Angular. The `OnPush` strategy only triggers change detection in a component when one of the following conditions is met:

  1. The input properties of the component change.
  2. An event emitted by the component itself or one of its child components is received.

To demonstrate this, let’s consider an example with a parent component and a child component, both using the `OnPush` change detection strategy:


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

@Component({
selector: 'app-parent',
template: `
<button (click)="triggerEvent()">Trigger Event</button>
<app-child [inputproperty]="inputProperty"></app-child>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentComponent {
inputProperty: string = 'Initial value';

triggerEvent() {
// Event triggered in the parent component
console.log('Event triggered in parent component');
}
}

// child.component.ts
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'app-child',
template: `
{{ inputProperty }}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() inputProperty: string;
}

In this example, the `ParentComponent` has a button that triggers the `triggerEvent()` method. However, since the `ParentComponent` and `ChildComponent` both use the `OnPush` change detection strategy, the event triggered in the parent component will not directly trigger change detection in the child component.

To propagate changes from the parent to the child component, you would need to update an input property of the child component. For example, you can modify the `triggerEvent()` method in the `ParentComponent` as follows:


triggerEvent() {
this.inputProperty = 'New value'; // Update input property of child component
}

By updating the input property value, Angular’s change detection mechanism will detect the change and trigger change detection within the child component with the `OnPush` strategy. This will update the child component’s view accordingly.

Conclusion :

To summarize, events triggered in the parent component do not directly cause change detection in a child component with the `OnPush` strategy. However, you can indirectly trigger change detection by updating an input property of the child component from the parent component.

Therefore, if an event is triggered in the parent component, it will not automatically trigger change detection in a child component with the `OnPush` strategy. This behavior is intentional to optimize performance by reducing unnecessary change detection cycles.

28. Can event triggered in child cause change detection in parent with OnPush strategy in Angular ?

No, an event triggered in a child component cannot directly cause change detection in the parent component with the `OnPush` change detection strategy in Angular. The `OnPush` strategy only triggers change detection in a component when one of its input properties changes or when an event emitted by the component itself or its child components is received.

However, you can propagate changes from the child component to the parent component using techniques like EventEmitter or a shared service. Here’s an example to illustrate how you can achieve this:


// child.component.ts
import { Component, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'app-child',
template: `
<button (click)="triggerEvent()">Trigger Event</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Output() childEvent: EventEmitter = new EventEmitter();

triggerEvent() {
this.childEvent.emit(); // Emit event from child component
}
}

// parent.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'app-parent',
template: `
<app-child (childevent)="handleChildEvent()"></app-child>
<p>Event Received: {{ eventReceived }}</p>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentComponent {
eventReceived: boolean = false;

handleChildEvent() {
this.eventReceived = true; // Update state in parent component
}
}

In this example, the `ChildComponent` emits a custom event using the `childEvent` EventEmitter when the button is clicked. The `ParentComponent` listens to this event and triggers the `handleChildEvent()` method, which updates the `eventReceived` property in the parent component.

Conclusion:

By updating the state in the parent component, Angular’s change detection mechanism detects the change and triggers change detection in the parent component, which will update the view accordingly.

Please note that while the change detection in the parent component is triggered in response to the event emitted by the child component, it is not directly caused by the event itself. Instead, it is the change in the parent component’s state that triggers the change detection.

29. How to optimize an Angular application ?

Optimizing an Angular application involves various strategies and techniques to improve its performance and efficiency. Here are some steps you can take to optimize your Angular application:

  1. Minify and bundle your code: Use a build tool like Angular CLI(ng build — prod) to minify and bundle your application code. This reduces the file size and improves the load time of your application.
  2. Lazy loading modules: Split your application into smaller modules and lazy load them when needed. This approach reduces the initial bundle size and improves the initial load time of your application.
  3. Use Ahead-of-Time (AOT) compilation: Enable AOT compilation in your Angular application. AOT compiles your templates during the build process, resulting in faster rendering and improved performance.
  4. Optimize network requests: Reduce the number of HTTP requests by combining multiple requests into a single one using techniques like HTTP batching or using server-side rendering (SSR) to pre-render pages. Implement caching mechanisms to store and reuse frequently accessed data.
  5. Optimize rendering: Avoid unnecessary re-rendering of components by using the `OnPush` change detection strategy and leveraging the `ChangeDetectionRef` API when needed.
  6. Use Angular Universal: Consider implementing server-side rendering (SSR) using Angular Universal. SSR improves the initial rendering time and can enhance search engine optimization (SEO) by providing fully rendered pages to search engine crawlers.
  7. Optimize Angular performance tools: Leverage Angular performance tools Angular DevTools, and Lighthouse to identify performance bottlenecks, memory leaks, and other issues in your application. Use the performance profiling features to analyze and optimize your code.
  8. Tree shaking and dead code elimination: Ensure that you have configured your build process to perform tree shaking and dead code elimination. This eliminates unused code from your application, resulting in a smaller bundle size.
  9. Optimize CSS and images: Optimize your CSS by reducing the number of selectors, removing unused styles, and minifying the CSS files. Use image sprite which is a collection of images put into a single image. A web page with many images can take a long time to load and generates multiple server requests. Using image sprites will reduce the number of server requests and save bandwidth.

Conclusion :

Remember that optimization is an iterative process, and the specific optimizations required may vary depending on the nature of your application. Monitor your application’s performance and gather feedback from users to identify areas that need further optimization.

30. Explain pure and impure pipe in Angular ?

In Angular, pipes are used to transform data in templates. They can be categorized as pure pipes and impure pipes based on their behavior and performance characteristics.

  1. Pure Pipes: Pure pipes are the default type of pipes in Angular. They are designed to be pure functions that take an input value and return a transformed output value. Pure pipes are stateless and deterministic, meaning their output is solely dependent on their input, and they don’t have any side effects. Angular optimizes pure pipes by executing them only when their input values change.

Here’s an example of a pure pipe in Angular:


import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'testPipe',
pure: true
})
export class TestPipe implements PipeTransform {
transform(value: string): string {
Object.keys(value).forEach(function(key,index) {
// key: the name of the object key
// index: the ordinal position of the key within the object
value[key] = value[key].toUpperCase()
});
return value;
}
}

In the above example, the `TestPipe` is a pure pipe that transforms a string to uppercase. The `pure: true` setting in the `@Pipe` decorator indicates that this pipe is pure. It will only execute the `transform` function when the `value` input changes.

Suppose we have HTML code as following:-

{{ user | testPipe}} in HTML 

and component code as following:-


user = { name:'test', city: 'test city'};

and the new changes are :-


this.user.city = "new test city"

For the above example, testPipe will not execute as object reference is not changed, To allow the pipe to execute , we have to make the pure attribute of testPipe as false or we need to make following changes in the component code:-


this.user = {
name: 'new test',
city: 'new test city'
}

In above code, testPipe will execute as object reference is changed.

2. Impure Pipes:

Impure pipes, on the other hand, are pipes that may have side effects and can be executed more frequently. They are explicitly marked as impure by setting the `pure` property to `false` in the `@Pipe` decorator. Impure pipes are not optimized by Angular for change detection and can be executed multiple times, even when the input values haven’t changed.

Here’s an example of an impure pipe in Angular:


import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'testPipe',
pure: false
})
export class TestPipe implements PipeTransform {
transform(value: string): string {
Object.keys(value).forEach(function(key,index) {
// key: the name of the object key
// index: the ordinal position of the key within the object
value[key] = value[key].toUpperCase()
});
return value;
}
}

Suppose we have HTML code as following:-

{{ user | testPipe }} in HTML 

and component code as following:-


user = { name:'test', city: 'test city'};

and the new changes are :-


this.user.city = "new test city"

For the above example, testPipe will execute, as Impure pipes executes every time angular detects any changes regardless of the change in the input value.

Conclusion:

It’s important to note that while pure pipes are the default and recommended type in Angular due to their performance optimizations, impure pipes can still be useful in certain scenarios when dealing with stateful or non-deterministic transformations. However, using impure pipes excessively or inappropriately can impact the performance of your Angular application.

31. How to test an Angular Service ?

To test an Angular service, you can use Angular’s testing utilities and techniques. Here’s an example that demonstrates how to test an Angular service:

Let’s assume you have a simple service called `UserService` that performs user-related operations:


import { Injectable } from '@angular/core';

@Injectable()
export class UserService {
private users: string[] = [];

addUser(user: string) {
this.users.push(user);
}

getUserCount() {
return this.users.length;
}

deleteUser(user: string) {
const index = this.users.indexOf(user);
if (index !== -1) {
this.users.splice(index, 1);
}
}
}

Now, let’s write a unit test for the `UserService` using Angular’s testing utilities:


import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';

describe('UserService', () => {
let service: UserService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(UserService);
});

it('should add a user', () => {
service.addUser('John');
expect(service.getUserCount()).toBe(1);
});

it('should delete a user', () => {
service.addUser('John');
service.addUser('Jane');
service.deleteUser('John');
expect(service.getUserCount()).toBe(1);
});
});

In the test above, we use the `describe``TestBed.configureTestingModule()`, and then inject the `UserService` using `TestBed.inject()` and assign it to the `service` variable.

The `it` function defines individual test cases. In the first test, we call the `addUser` method with the user ‘John’ and expect the user count to be 1. In the second test, we add two users, ‘John’ and ‘Jane’, then call the `deleteUser` method with ‘John’ and expect the user count to be 1.

To run the unit tests for the service, you can use the Angular CLI command `ng test`, which executes the tests using Karma test runner.

Conclusion :

By writing tests for your Angular services, you can ensure that the service functions as expected, including its methods and data manipulations. It helps you catch errors, verify the behavior, and maintain the correctness of your service logic.

32. Explain ngAfterContentInit hooks ?

The `ngAfterContentInit` hook is a lifecycle hook in Angular that is called after Angular initializes the content projected into a component. This hook is useful when you want to perform some initialization or setup logic after the content has been projected into the component.

Here’s an example of how you can use the `ngAfterContentInit` hook in an Angular component:


import { Component, AfterContentInit, ContentChild } from '@angular/core';

@Component({
selector: 'app-my-component',
template: `
<ng-content></ng-content>
`
})
export class MyComponent implements AfterContentInit {
@ContentChild('myContent') myContent: ElementRef;

ngAfterContentInit() {
// This code will run after the content has been projected into the component
console.log('Content initialized:', this.myContent.nativeElement.textContent);
}
}

n this example, the `MyComponent` component has a template that includes the `<ng-content></ng-content>` tag. This tag is a placeholder where the content will be projected when the component is used.

Inside the component class, we use the `@ContentChild` decorator to get a reference to the projected content. In this case, we’re looking for an element with the template reference variable `myContent`. You can use other selectors, such as CSS classes or component types, depending on your specific use case.

The `ngAfterContentInit` method is implemented as part of the `AfterContentInit` interface, which allows us to hook into the lifecycle of the component. Inside this method, you can perform any necessary initialization or logic based on the projected content. In this example, we log the text content of the projected element to the console.

When you use the `MyComponent` component in another template and provide content to be projected, the `ngAfterContentInit` method will be called after the content has been initialized.


<app-my-component>
<div #mycontent>This content will be projected</div>
</app-my-component>

When the above code runs, the `ngAfterContentInit` method in `MyComponent` will be triggered, and it will log the text content of the projected `<div>` element to the console.

Remember to include the necessary imports for `Component`, `AfterContentInit`, and `ContentChild` from the `@angular/core` module in your Angular component.

33. Explain ngAfterViewInit hook with example ?

The `ngAfterViewInit` hook is a lifecycle hook in Angular that is called after Angular initializes the component’s view and its child views. This hook is useful when you need to perform some logic or operations that require access to the component’s view or its child views.

Here’s an example of how you can use the `ngAfterViewInit` hook in an Angular component:


import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';

@Component({
selector: 'app-my-component',
template: `
<div #myDiv>Some content</div>
`
})
export class MyComponent implements AfterViewInit {
@ViewChild('myDiv') myDiv: ElementRef;

ngAfterViewInit() {
// This code will run after the component's view has been initialized
console.log('View initialized:', this.myDiv.nativeElement.textContent);
}
}

In this example, the `MyComponent` component has a template that includes a `<div>l` element with the template reference variable `myDiv`. We use the `@ViewChild`decorator to get a reference to this element in the component class.

The `ngAfterViewInit` method is implemented as part of the `AfterViewInit` interface, which allows us to hook into the lifecycle of the component’s view. Inside this method, you can access and manipulate the DOM elements of the component’s view. In this example, we log the text content of the `

` element to the console. When the component’s view is initialized, Angular calls the `ngAfterViewInit` method, and you can perform any necessary operations that require access to the component’s view or its child views.

Here’s an example of using the `MyComponent` component in another template:


<app-my-component></app-my-component>

When the above code runs, the `ngAfterViewInit` method in `MyComponent` will be triggered, and it will log the text content of the `<div>` element to the console.

Remember to include the necessary imports for `Component`, `AfterViewInit`, `ViewChild`, and `ElementRef` from the `@angular/core` module in your Angular component.

Conclusion:

Note: It’s important to use the `ngAfterViewInit` hook with caution, as accessing and manipulating the DOM directly can go against the principles of Angular’s declarative approach. Whenever possible, it’s recommended to use Angular’s data binding and component interaction mechanisms instead of manipulating the DOM directly.

34. Difference between ngAfterContentInit and ngAfterViewInit ?

The `ngAfterContentInit` and `ngAfterViewInit` hooks are both lifecycle hooks in Angular, but they are used in different contexts and serve different purposes.

`ngAfterContentInit` is a lifecycle hook that is called after Angular initializes the content projected into a component. It is used when you need to perform initialization or setup logic that depends on the content projected into the component. This hook is typically used in components that have `<ng-content></ng-content>` tags in their templates to project content from the parent component. You can access the projected content using the `@ContentChild` decorator.

On the other hand, `ngAfterViewInit` is a lifecycle hook that is called after Angular initializes the component’s view and its child views. It is used when you need to perform logic or operations that require access to the component’s view or its child views. This hook is often used for DOM manipulation, accessing ViewChild elements, or interacting with third-party libraries that require the view to be fully rendered. You can access the view elements using the `@ViewChild` decorator.

Here’s a summary of the key differences between `ngAfterContentInit` and `ngAfterViewInit`:

  1. Purpose:

— `ngAfterContentInit`: Used for initialization or setup logic dependent on projected content.

— `ngAfterViewInit`: Used for logic or operations that require access to the component’s view or child views.

2. Timing:

— `ngAfterContentInit`: Called after content projection is initialized.

— `ngAfterViewInit`: Called after the component’s view and child views are initialized.

3. Decorators:

— `ngAfterContentInit`: Use `@ContentChild` decorator to access projected content.

— `ngAfterViewInit`: Use `@ViewChild` decorator to access view elements.

4. Usage:

— `ngAfterContentInit`: Typically used in components that project content from parent components.

— `ngAfterViewInit`: Typically used for DOM manipulation, accessing view elements, or interacting with third-party libraries.

Conclusion:

In summary, `ngAfterContentInit` is used when you want to perform initialization based on projected content, while `ngAfterViewInit` is used when you need to perform logic or operations that require access to the component’s view or child views.

35. What is View Encapsulation in Angular ?

In Angular, ViewEncapsulation is a feature that controls the way styles are applied and scoped to components. It is a mechanism that encapsulates the styles defined in a component to prevent them from affecting other components in the application.

By default, Angular uses the ViewEncapsulation.Emulated mode, also known as the “shadow DOM” emulation. In this mode, Angular emulates the behavior of the shadow DOM by adding a unique attribute to the component’s HTML elements and applying styles with these attributes. This way, styles defined in a component only affect the elements within that component’s template and do not leak out to other parts of the application.

There are three ViewEncapsulation modes available in Angular:

  1. ViewEncapsulation.Emulated (default): This mode emulates the shadow DOM by adding unique attributes to the component’s elements. The styles defined within the component’s template are scoped to that component only. ViewEncapsulation.Emulated will add the css style in the head section of your website and reference your component’s unique id(_ngcontent) to apply it.
  2. ViewEncapsulation.None: In this mode, styles defined in a component’s template are not encapsulated and can affect the entire application. It’s important to use this mode with caution, as it can lead to style collisions and unintended side effects when multiple components use the same styles.
  3. ViewEncapsulation.ShadowDom: This mode uses the native browser’s shadow DOM implementation to encapsulate the styles. It requires the browser to support the shadow DOM. With this mode, the component’s styles are truly isolated within the component and do not leak out to other components or the global styles. ViewEncapsulation.ShadowDom will add the css style inside the generated DOM of your component.

To specify the ViewEncapsulation mode for a component, you can use the `encapsulation` property in the component’s metadata:


import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css'],
encapsulation: ViewEncapsulation.Emulated // or ViewEncapsulation.None or ViewEncapsulation.ShadowDom
})
export class MyComponent {
// Component logic goes here
}

Conclusion:

By understanding and utilizing ViewEncapsulation in Angular, you can have better control over styles and prevent unintended style interference between components in your application.

36. What is the use of polyfills ?

Polyfills are additional scripts that provide compatibility for features or APIs that are not supported by all browsers. These scripts are automatically included in an Angular application during the build process to ensure that the application can run on a wide range of browsers, including older versions.

Here’s an example to illustrate the usage of polyfills in an Angular application:

  1. Open the “polyfills.ts” file: In your Angular project, locate the “polyfills.ts” file, which is usually located in the “src” folder. This file contains the configuration for the polyfills.
  2. Uncomment or add the required polyfills: Inside the “polyfills.ts” file, you’ll find a section with a list of polyfills that can be uncommented or added as needed. Each polyfill corresponds to a specific feature or API that may not be supported by all browsers.

For example, if you need to support older browsers that lack support for certain JavaScript features, you can uncomment or add the polyfill for the specific feature. Let’s say you want to provide support for the `Array.from` method, which might not be available in all browsers. You can uncomment or add the following line:


// Uncomment below to enable support for 'Array.from' in older browsers
// import 'core-js/es/array/from';

This line imports the polyfill from the “core-js” library to ensure that the `Array.from` method works in browsers that don’t support it natively.

Similarly, you can uncomment or add other polyfills based on the features or APIs you need to support.

3. Build and deploy the application: After configuring the required polyfills in the “polyfills.ts” file, you can proceed to build and deploy your Angular application as usual using the Angular CLI or your preferred build process.

During the build process, Angular will bundle the application along with the specified polyfills. These polyfills will be automatically loaded in the browser alongside your application code.

Conclusion:

By including the appropriate polyfills, your Angular application can leverage modern features and APIs while maintaining compatibility with a wider range of browsers, including older versions. The polyfills ensure that the application behaves consistently across different browsers, providing a smooth and functional user experience.

Remember to review and update your polyfills periodically to ensure compatibility with evolving browser standards and to optimize the performance of your application.

37. What is scan operator in RxJS ?

In RxJS (Reactive Extensions for JavaScript), the scan operator is used to perform an accumulation operation on the values emitted by an observable sequence. It resembles the Array.prototype.reduce() function in JavaScript but operates on an observable stream of values instead of an array.

The scan operator takes an accumulator function and an optional seed value as its parameters. The accumulator function is called for each value emitted by the source observable, and it accumulates the intermediate result based on the previous accumulated value and the current value from the source. The accumulated value is then emitted to the resulting observable sequence.

Here’s the general syntax of the scan operator:


const $scanOperator = from([1,2,3,4]).pipe(
scan((sum,num) => sum+num)
);

$scanOperator.subscribe(data=> {
console.log('scan operator data', data);
});

// output :
// scan operator data 1
// scan operator data 3
// scan operator data 6
// scan operator data 10

Note: In scan operator, intermediate result is emitted while in reduce operator intermediate result is not emitted and only the final result is emitted.

Conclusion :

The scan operator is useful for scenarios where you need to maintain state or accumulate values over time, such as tracking the total count, calculating averages, or simulating a running total.

38. How does spyOn work in Angular ?

In Angular, `spyOn` is a utility provided by the Jasmine testing framework, which is used to create test spies. Spies allow you to observe and control the behavior of functions during testing. They are commonly used to replace certain function implementations with custom ones, track function calls, and assert on their behavior.

Here’s an example to demonstrate how `spyOn` works in Angular:

Let’s assume you have a service called `DataService` that interacts with an external API:


import { Injectable } from '@angular/core';

@Injectable()
export class DataService {
fetchData(): Promise {
return new Promise((resolve) => {
// Simulating an asynchronous API call
setTimeout(() => {
const data = 'Some data from API';
resolve(data);
}, 1000);
});
}
}

Now, let’s write a unit test for the `DataService` using `spyOn` to mock the API call:


import { TestBed } from '@angular/core/testing';
import { DataService } from './data.service';

describe('DataService', () => {
let service: DataService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DataService);
});

it('should fetch data from the API', () => {
const apiResponse = 'Mocked API response';
spyOn(service, 'fetchData').and.returnValue(Promise.resolve(apiResponse));

service.fetchData().then((data) => {
expect(data).toBe(apiResponse);
expect(service.fetchData).toHaveBeenCalled();
});
});
});

In the test above, we use the `spyOn` function to create a spy on the `fetchData` method of the `DataService`. The `and.returnValue` method is used to specify the return value of the spy, which is a resolved promise with the mocked API response.

Then, we call the `fetchData` method and assert that the returned data matches the mocked API response using the `expect` function. Additionally, we use the `toHaveBeenCalled` matcher to verify that the `fetchData` method was called.

By using `spyOn`, we can replace the original implementation of the `fetchData` method with a mock implementation and control its behavior during testing. This allows us to isolate the service and focus on testing its interactions without making actual API calls.

Remember to import the necessary testing utilities (`TestBed`, `spyOn`, etc.) from the appropriate packages (`@angular/core/testing`, `jasmine`, etc.) in your test files.

Note: It’s important to mention that `spyOn` is not specific to Angular but is part of the Jasmine testing framework, which is commonly used in Angular unit testing.

39. How to mock http request in Angular ?

To mock HTTP requests in Angular unit tests, you can use the `HttpClientTestingModule` and `HttpTestingController` provided by Angular’s testing utilities.

Here’s an example to demonstrate how to mock an HTTP request in Angular:

Let’s assume you have a service called `DataService` that makes HTTP requests using Angular’s `HttpClient`:


import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class DataService {
constructor(private http: HttpClient) {}

fetchData(): Promise<string> {
return this.http.get<string>('https://api.example.com/data').toPromise();
}
}

Now, let’s write a unit test for the `DataService` and mock the HTTP request:


import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { DataService } from './data.service';

describe('DataService', () => {
let service: DataService;
let httpMock: HttpTestingController;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [DataService]
});
service = TestBed.inject(DataService);
httpMock = TestBed.inject(HttpTestingController);
});

afterEach(() => {
httpMock.verify();
});

it('should fetch data from the API', () => {
const mockResponse = 'Mocked API response';

service.fetchData().then((data) => {
expect(data).toBe(mockResponse);
});

const req = httpMock.expectOne('https://api.example.com/data');
expect(req.request.method).toBe('GET');
req.flush(mockResponse);
});
});

In the test above, we import `HttpClientTestingModule` and use it in the `TestBed.configureTestingModule()` to configure the testing module. We also inject the `HttpTestingController` to interact with the HTTP requests.

Inside the test, we call the `fetchData` method of the `DataService` and assert on the response data. Then, we use `httpMock.expectOne()` to intercept the HTTP request and return a mock response using `req.flush()`. Finally, we use `httpMock.verify()` in the `afterEach` block to ensure that no unexpected requests were made.

Conclusion :

By using the `HttpClientTestingModule` and `HttpTestingController`, we can mock the HTTP requests and control their responses, allowing us to test the behavior of the service without making actual API calls. Note: Make sure to import the necessary testing utilities (`TestBed`, `HttpClientTestingModule`, `HttpTestingController`, etc.) from the appropriate packages (`@angular/core/testing`, `@angular/common/http/testing`, etc.) in your test files.

40. What is reduce operator in RxJS ?

In RxJS, the reduce operator is used to apply an accumulation function to the values emitted by an observable sequence and emit a single accumulated result. It is similar to the Array.prototype.reduce() function in JavaScript.

The reduce operator takes an accumulator function and an optional seed value as its parameters. The accumulator function is called for each value emitted by the source observable, and it accumulates the intermediate result based on the previous accumulated value and the current value from the source. The accumulated value is then emitted as the final result when the source observable completes.

Here’s the general syntax of the reduce operator:


const $reduceOperator = from([1,2,3,4]).pipe(
reduce((sum,num) => {
return sum+num;
})
);

$reduceOperator.subscribe(data=> {
console.log('reduce operator data', data);
})

// output
// reduce operator data 10

Note: In reduce operator, intermediate result is not emitted and only the final result is emitted, while in scan operator intermediate result is emitted.

Conclusion:

The reduce operator is useful when you want to obtain a single accumulated result from a sequence of values. It is commonly used for calculations that produce a final output, such as summing values, finding the maximum or minimum, or performing any other reduction operation on the emitted values.

41. What is the use of trackBy in Angular ?

In Angular, the `trackBy` function is used in conjunction with the `ngFor` directive to improve performance when rendering lists of items. By providing a `trackBy` function, Angular can track and update individual items in the list instead of re-rendering the entire list whenever a change occurs.

The `trackBy` function takes two arguments: the index of the item in the list and the item itself. It should return a unique identifier for each item. Angular uses this identifier to track the items and determine whether they have changed or moved within the list.

Here’s an example of how to use `trackBy` in an `ngFor` loop:


<!-- my-component.component.html -->
<ul>
<li *ngfor="let item of items; trackBy: trackByFn">{{ item.name }}</li>
</ul>

// my-component.component.ts
import { Component } from '@angular/core';

@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent {
items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
];

trackByFn(index: number, item: any): number {
return item.id; // Unique identifier for each item
}
}

In this example, the `trackByFn` function returns the `id` property of each item as the unique identifier. Angular uses this identifier to track the items and update the DOM efficiently when changes occur.

By using `trackBy`, Angular avoids re-rendering and updating the entire list when an item is added, removed, or moved. Instead, it only updates the specific item that has changed, resulting in better performance and a smoother user experience, especially when dealing with large lists.

Note:

It’s important to note that the unique identifier returned by the `trackBy` function must remain stable for each item. If the identifier changes when an item is updated, Angular will treat it as a new item and re-render it. Therefore, it’s recommended to use an identifier that doesn’t change over the item’s lifetime, such as a unique ID from your data source.

Note:- You are expected to know

- Angular Interview Questions For Freshers

- Angular Interview Questions For 3 Years Of Experience

--

--

Pravin M

I am a frontend developer with 10+ years of experience