Tech Point Fundamentals

Saturday, January 13, 2024

Angular Interview Questions and Answers - Part 17

Angular Interview Questions and Answers - Part 17

Angular-Interview-Questions-And-Answers

Angular is a popular open-source single-page application design framework and development platform. It is a robust front-end JavaScript framework that is widely used for front-end application development. Nowadays Angular is a very common frontend technology so it is very important for the interview point as well. 

Introduction


This is the Angular Interview Questions and Answers series. Here we will see 200+ Most Frequently Asked Angular Interview Questions. This is the 17th part of the Angular Interview Questions and Answer series.

I will highly recommend to please do visit the following parts before continuing to this part:



Please do visit our YouTube Channel here to watch Interview Questions & Answers and important technology videos.

In this part we will discuss the following Important Angular Interview Questions:

Q194. How does Angular DI Framework Resolve the Dependencies?
Q195. What is the difference between a Module Injector vs. an Element Injector?
Q196. What is a Resolution Modifier in Angular? What are the different types of Resolution Modifiers?
Q197. What is a DI token in Angular? 
Q198. What are the different Resolution Modifiers?
Q199. What is a Type Token in Angular?
Q200. What is a String Token in Angular DI? What is the main limitation of string tokens?
Q201. What is an Injection Token in Angular? What is the difference between OpaqueToken vs InjectionToken?
Q202. What is the forRoot() method in Angular? How it is different from forChild() method?

Angular Interview Questions and Answers: Part 17


Q194. How does Angular DI Framework Resolve the Dependencies?

The Angular creates a hierarchical dependency injection system. It creates a hierarchical tree of Injectors. The older versions of Angular were creating only one Injector tree. But there was some major issue in that. So in the later versions, the tree was split into two trees.  One is ElementInjector for elements (components, directives & pipes, etc.) and the other one is ModuleInjector for Angular Modules. 

When a component asks for Dependency, the DI Framework resolves it in two phases.

  1. First, Angular try to resolve it using the Element Injector and its parents
  2. If it is not found in the Element Injector, then resolve it against the Module Injector and its parents.

In the first phase, it starts to look for the Dependency in the current component’s ElementInjector. If it does not find the Dependency, it will look in the Parent Components ElementInjector. The Request bubbles up until it finds an injector that provides the service or reaches the root ElementInjector.

If ElementInjector does not satisfy the request, Angular looks for the Dependency in the ModuleInjector hierarchy. If Angular still doesn’t find the provider, it throws an error.

DI-resolution-angular
Source: indepth.dev


Phase 1: Element Injector Search:

When a component declares a dependency, Angular tries to satisfy that dependency with its own ElementInjector. If the component's injector lacks the provider, it passes the request up to its parent component's ElementInjector. The requests keep forwarding up until Angular finds an injector that can handle the request or runs out of ancestor ElementInjector hierarchies.

The search starts at the Injector associated with the component in the Element Injector tree. It uses the token to search for dependency in its Providers array. If it finds the provider, the Injector checks to see if the instance of the service already exists. If exists then it injects it into the component else it creates a new instance of it. Then it injects it into the component.

Then Injector passes the request to the parent Injector in the Element Injector Hierarchy if it fails to find the provider. If the provider is found, the request returns the instance of the Provider. If not found then the request continues until the request reaches the topmost injector in the Element Injector tree.

The topmost Injector in the Element Injector tree belongs to the root component. If the dependency is not found, it does not throw the error but returns back.

Phase 2: ModuleInjector Search:

If Angular doesn't find the provider in any ElementInjector hierarchies, it goes back to the element where the request originated and looks in the ModuleInjector hierarchy. If Angular still doesn't find the provider, it throws an error.

The search shifts to the ModuleInjector Tree if it is not found in the ElementInjector Tree. Here the search starts from the Injector associated with the module to which the element belongs. For the Root Modules and Eagerly loaded Module, the search starts from the Root Module Injector. However, for the components from the lazy-loaded modules, the resolutions start from the Module Injector associated with the loaded Module.

The request continues until the request reaches the topmost injector in the Module Injector tree i.e. Null Injector. The Null Injector does not contain any providers. Its job is to throw a No Provider for Service error. But if we decorate the dependency with @Optional decorator, then it will return null instead of throwing an error. So now angular will attempt to resolve dependency in the following way:

AppModule Injector => ZoneInjector  => Platform Injector => NullInjector  => Error

Tech Points:

  1. The ModuleInjector tree is not a parent of the Element Injector tree. Each Element can have the same parent in the Element Injector Tree, but a different parent in Module Injector Tree.
  2. There are no Injectors from Eager Modules. They share it with the Root Module. But Separate Injector for Lazy Loaded Modules in Module Ejector Tree.
  3. If two Eager Modules, provide the service for the same token, the module, which appears last in the imports array wins. For example imports: [ FirstEagerModule, SecondEagerModule]; Here providers of SecondEagerModule overwrite the providers of the FirstEagerModule for the same take.
  4. If the Eager Module & Root Module provide the service for the same token, then the Root Module always wins.
  5. Any service with providedIn value of root in the lazily loaded module, become part of the Root Module Injector. To restrict service to the lazily loaded module, remove it from the providedIn and add it to the providers array of the Module.
  6. The Services are singletons within the scope of an injector. When the injector gets a request for a particular service for the first time, it creates a new instance of the service. For all the subsequent requests, it will return the already created instance. 
  7. The Injectors are destroyed when Angular removes the associated Module or element.
  8. Where you configure your services, will decide the service scope, service lifetime & bundle size. You can use Resolution Modifiers to modify the behavior of injectors like  @Self, @SkipSelf & @Optional Decorators & @Host Decorator in Angular

Q195. What is the difference between a Module Injector vs. an Element Injector?

Angular creates the ModuleInjector for the services to be provided at Module Levels.  Module Injector tree is created for Modules (@NgModule), Root Module & for every Lazy Loaded Modules. Angular creates the Module Injector tree when the Application starts.

On the other hand, Angular creates ElementInjector hierarchies implicitly for each DOM element. The Element Injectors are created for the services to be provided at the element level like Components and directives. The Element Injector tree is also created when the application starts.

Angular parses the template to create a factory with a view definition. The view is just a representation of a template, which contains different types of nodes such as directive, text, provider, query, etc. Among others, there is an element node. Actually, the element injector resides on this node. Angular keeps all information about providers on an element node.

It just checks allProviders or publicProviders properties depending on privacy. This injector contains the component/directive instance and all the providers registered by the component or directives. These providers are filled during view instantiation, but the main source comes from ProviderElementContext, which is a part of the Angular compiler.

One important scenario is that if we have an element with a component and directive applied to it, and we provide the same token on the component and on the directive, then the directive’s provider wins.

Q196. What is a Resolution Modifier in Angular? 

By default, Angular always starts at the current Injector and keeps searching all the way up. Resolution  Modifiers allow you to change the starting, or self, location and the ending location. 

Actually, Angular starts to look for the Dependency in the current component’s ElementInjector. If it does not provide the Dependency, it will look in the Parent Components ElementInjector. The Request bubbles up until it finds an injector that provides the service or reaches the root ElementInjector. 

If ElementInjector does not satisfy the request, Angular looks for the dependency in the ModuleInjector hierarchy. If Angular still doesn’t find the provider, it throws an error. This behavior of DI resolution can be modified by resolution modifiers.

@Self, @SkipSelf, @Optional & @Host are Angular Decorators that configure how the DI Framework should resolve the dependencies. These decorators are called Resolution Modifiers because they modify the behavior of injectors.

Angular's default dependency resolution behavior can be modified with @Optional(), @Self(), @SkipSelf(), and @Host(). They can be imported from @angular/coreNow you can use each in the component class constructor when you inject your service. 

Q197. What are the different Resolution Modifiers?

Types of Resolution Modifiers:

There are three categories of Resolution modifiers:

  1. What to do if Angular doesn't find what you're looking for: @Optional()
  2. Where to start looking: @SkipSelf()
  3. Where to stop looking: @Host() and @Self()

You can combine all of the modifiers except @Host() and @Self() and of course @SkipSelf() and @Self().

@Optional() Resolution Modifier

@Optional marks the dependency as Optional. If the dependency is not found, then it returns null instead of throwing an error. If you add the @Optional decorator along with the @Self. Now, the dependency injection will return null instead of an error.

The @Optional() allows Angular to consider a service you inject to be optional. This way, if it can't be resolved at runtime, Angular resolves the service as null, rather than throwing an error.

@Self() Resolution Modifier: 

The @Self decorator instructs Angular to look for the dependency only in the local injector. The local injector is the injector that is part of the current component or directive. 

We can use @Self() so that Angular will only look at the ElementInjector for the current component or directive. It forces the Angular DI Framework to look for the Dependency attached to the current Component. Since it does find one it will throw the error. For example, if you want to inject a service but only if it is available on the current host element. 

To avoid errors in this situation, combine @Self() with @Optional(). Injecting the service with @Self() and @Optional() will return null because @Self() tells the injector to only search in the current host element.

@SkipSelf() Resolution Modifier: 

The @SkipSelf decorator instructs Angular to look for the dependency in the Parent Injector and upwards. It tells Angular not to look for the injector in the local injector, but to start from the Parent. 

The @SkipSelf() is just the opposite of @Self(). With @SkipSelf(), Angular starts its search for a service in the parent ElementInjector, rather than in the current one. We can use @SkipSelf() with @Optional() to prevent an error if the value is null.

@Host() Resolution Modifier: 

@Host() lets you designate a component as the last stop in the injector tree when searching for providers. Even if there is a service instance further up the tree, Angular won't continue looking.


Q198. What is a DI token in Angular? What are the different types of DI token that Angular use?

In Angular, a Dependency Injection token is a unique identifier that Angular uses to locate the provider of a dependency. A Type Token is the type of class that you want to inject. DI system in Angular uses tokens to uniquely identify a Provider. 

For example, if you want to inject an instance of UserService, you can use UserService as the token. You can also use the same token to inject another implementation of UserService.

Angular Providers allows us to register classes, functions, or values with the Angular Dependency Injection system. The Providers are registered using the token. The tokens are used to locate the provider. We declare the Provider with providers metadata. 

 providers :[{ provide: UserService, useClass: UserService }]

In the above provider declaration, the "Provide" holds the Token or DI Token. The tokens act like a key. The DI systems need this key to locate provider in the Providers array. The Token can be either a type, a string, or an instance of InjectionToken.
Types of Dependency Injection Tokens:

There are three types of tokens that you can create in Angular. 

  1. Type Token
  2. String Token
  3. Injection Token

Q199. What is a Type Token in Angular?

In Angular, a Type Token is the type of class that you want to inject. In this case, the type being injected is used as the token.

For example, if you would like to inject the instance of the UserService, you can use the UserService as the Token:

 providers :[{ provide: UserService, useClass: UserService }]

The UserService is then injected into the component:

class ProductComponent {
  constructor(private userService : UserService ) {}
}

You can also use the same token to inject another implementation of UserService. You can keep the same token (UserService) and change the class to another implementation of the User service. 

Also, Angular does not complain if we use the token again. In such a situation last to register wins.

  providers :[
      { provide: UserService, useClass: UserService },
      { provide: UserService, useClass: AdminService }]


Q200. What is a String Token in Angular DI? What is the main limitation of string tokens?

Instead of using a type, we can use a string literal to register the dependency. This is useful in scenarios where the dependency is a value or object etc, which is not represented by a class.

String Token is a string that you use as a token. You can use string tokens when you need to inject simple string values or simple object literals where there is no type.

You can use the Type token only if you have Type representation. But that is not always the case. Sometimes we must inject simple string values or simple object literal, where there is no type. We can use string tokens in such a scenario.

For example, you can use the string 'USER_SERVICE' as a token to inject an instance of UserService:

providers: [{ provide: 'USER_SERVICE', useClass: UserService }]

You can then use Inject the UserService using the @Inject method:

export class AppComponent {
  users: User[];  
  constructor(
  @Inject('USER_SERVICE') private userService: UserService
  ) {}


Limitations of String Token:

The String tokens are easy to use but more prone to error. Two developers can use the same token at different parts of the app.  You also do not have any control over the third-party modules, which may use the same token.  

If we re-use the token, the last to register overwrites all previously registered tokens. String tokens are easier to mistype, making it difficult to track & maintain in big applications. This is where the InjectionToken comes into the picture.

Q201. What is an Injection Token in Angular? What is the difference between OpaqueToken vs InjectionToken?

The Injection Token allows us to create a token to inject the values that don’t have a runtime representation. It is very similar to string tokens, but instead of using a hardcoded string, we create the Injection Token by creating a new instance of the InjectionToken class. They ensure that the tokens are always unique.

Angular provides InjectionToken class to ensure that the Unique tokens are created. The Injection Token is created by creating a new instance of the InjectionToken class.

OpaqueToken vs InjectionToken:

In Angular, an OpaqueToken is a class that is used as a unique identifier for injector providers. It allows you to create string-based tokens without running into any collisions. You can use it when you need to inject a value that doesn’t have a corresponding type or when you want to provide multiple values for the same token.

The Angular 4 and prior versions used OpaqueToken. However, in Angular 4, OpaqueToken was marked as deprecated and replaced by InjectionToken.  An injectionToken allows you to pass a generic type parameter.

To create an Injection Token, first, we need to import the InjectionToken from @angular/core.  Then you can create a new Injection Token API URL from InjectionToken

import { InjectionToken } from '@angular/core';
export const APIURL = new InjectionToken('');

The code above creates a new instance of the InjectionToken API URL. creates a new injection token without type and an empty string as its description.

export const APIURL = new InjectionToken<string>('Api.url');

You can also specify the optional type parameter <string>, and the token description, "Api.url". Use the token description to specify the token’s purpose. 

Now you can register it in the providers array and inject it into the component:

providers: [{ provide: APIURL, useValue: 'http://techpointfunda.com/api' }]

export class AppComponent {
  constructor(@Inject(APIURL) public ApiUrl: string,) { }
}

Q202. What is the forRoot() method in Angular? How it is different from forChild() method?

The forRoot() static method is a convention that makes it easy for developers to configure services and providers that are intended to be singletons. A good example of forRoot() is the RouterModule.forRoot() method.

Only call and import a forRoot() result in the root application module i.e. AppModule. Avoid importing it in any other module, particularly in a lazy-loaded module. 

The forRoot() import can be used in a module other than AppModule. Importantly, forRoot() should only be called once, and the module that imports the forRoot() needs to be available to the root ModuleInjector.

For a service, instead of using forRoot(), specify providedIn: 'root' on the service's @Injectable() decorator, which makes the service automatically available to the whole application and thus singleton by default.

The forRoot() and forChild() are conventional names for methods that configure services in root and feature modules respectively. Follow this convention when you write similar modules with configurable service providers.




Recommended Articles




Thanks for visiting this page. Please follow and join us on  LinkedInFacebookTelegramQuoraYouTubeTwitterInstagramWhatsApp, VKTumbler, and Pinterest for regular updates.

No comments:

Post a Comment

Please do not enter any HTML. JavaScript or spam link in the comment box.