Tech Point Fundamentals

Saturday, December 23, 2023

Angular Interview Questions and Answers - Part 14

Angular Interview Questions and Answers - Part 14

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 14th 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:

Q160. How can you register a service at Component Level in Angular?
Q161. How can you register a service at the Module Level in Angular?
Q162. What is the difference between providing services in modules vs components?
Q163. How can you register a service into another service in Angular?
Q164. What is the Service Scope in Angular?
Q165. What is the difference between the service registered as root vs platform type in the ProvidedIn property of Angular?
Q166. What is the difference between a service registered as "root" vs "any" type in the ProvidedIn property of Angular?
Q167. What is the difference between providedIn: root vs forRoot() pattern in Angular?
Q168. What are the different ways to create Singleton Services in Angular?
Q169. What is an Element Injector Tree in Angular?
Q170. What is the Merge Injector in Angular?

Angular Interview Questions and Answers: Part 14


Q160. How can you register a service at Component Level in Angular?

There are multiple ways to limit the scope of a service in Angular. One way to limit the provider scope is by adding the service you want to limit to the component's provider arrayComponent's providers array and NgModule providers array are independent of each other. This method is helpful when you want to eagerly load a module that needs a service all to itself. 

If you want each component to have its own instance of the service, just import the service and provide the service directly in the component’s providers array.

Providing a service in the component limits the service only to that component and its descendants. Other components in the same module can't access it.

You can create a service without @Injectable and ProvidedIn property like this:

export class UserService {
  userName = "Tech Point Fundamentals";
  constructor() {
    console.log('User Service instance created!');
  }
  getUserName(): string {
    return this.userName;
  }
}

Now you can inject it into your component like this:

import { UserService } from '../user-service.service';

@Component({
  selector: 'app-user-component',
  templateUrl: './user-component.component.html',
  styleUrls: ['./user-component.component.scss'],
  providers: [UserService]
})
export class UserComponentComponent {
  Name: string = '';
  constructor( private userService: UserService) {
    this.Name = this.userService.userName;
  }
}

Q161. How can you register a service at the Module Level in Angular?

In applications built with Angular versions prior to 6.0, services are registered NgModule's providers arrays. To register a service at the module level, just import the service in the module.ts file and add the service to providers array of @NgModule of that module. Now, the service will be available only within that specific module and its child modules.

The dependencies can also be registered with the Providers array. This is done in the Providers metadata of the Injector. We can also add the services to the Providers array of the @NgModule. Then they will be available for use in all the components & services of the application.

It's also possible to specify that a service should be provided in a particular @NgModule. This method is preferred because it enables tree-shaking of the service if nothing is injected. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module.

For example, if you don't want UserService to be available to applications unless they import a UserModule you've created, you can specify that the service should be provided in the module:

import { UserService } from '../user-service.service';

@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ],
  providers: [UserService],
})
export class UserModule{ }

You can now use the service in any component of that module. Just import the service in the component.ts file and include it in the constructor of the component where you want to use it.

Q162. What is the difference between providing services in Modules vs Components?

When it comes to Angular, a service can be provided either at the component level or at the module level. The main difference between providing services in modules versus components is the scope of the service. 

If you provide a service at the component level, it will be local to that component only and will not be shared with other components. So if you provide your service inside your component, it will be local to it. So if you have two instances of your component, you will have two instances of your service. If you provide a service at the component level, it will only be available to that component and its child components.

On the other hand, if you provide a service at the module level, it will be available to all components that are part of that module. This means that if you have multiple components that require the same service, you can provide it once at the module level and all components will have access to it.  So if you provide your service inside your module, it will be global and if you have two instances of your component, they will share the same instance of the service.

In general, it is recommended to provide services at the module level if they are required by multiple components. This helps keep your code organized and avoids duplication of code.

What if the module is a root module?

Normally, we provide services that the whole application needs in the root module and scope services by providing them in lazy-loaded modules.  But some of the service like the router only works at the root level, so if you put providers in a component, even AppComponent, lazy loaded modules, which rely on the router, can't see them.

In Angular, services are registered in the NgModule providers arrays as:

@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ],
  providers: [UserService],
})
export class UserModule{ }

If this NgModule acts as the root AppModule, the UserService would be a singleton instance and available all over the application. So you can see it coded this way, using the providedIn property of the @Injectable() decorator on the service itself. It is preferable since  Angular 6.0 because it makes your services tree-shakable.

So you should register a provider with a component when you must limit a service instance to a component and its component tree, that is, its child components.

Q163. How can you register a service into another service in Angular?

We can inject a service into another service as well. Usually, we do not have to use the @Injectible if a class does not have any dependencies. We are also using @Injectible metadata to decorate our logger service class. Technically, we do not have to do that as the logger service does not have any external dependencies. 

However, it is best practice to decorate every service class with @Injectable(), even those that don’t have dependencies for reasons like Future proofing and Consistency.

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

@Injectable()

export class LoggerService {
  log(message:any) {
    console.log(message);
  }
  constructor() { }
}

Now suppose we need to inject this logger service into the user service:

import { LoggerService } from './logger.service';

export class UserService {  
  constructor(private loggerService: LoggerService) {
    console.log('User Service instance created!');
  }
}


Q164. What is the Service Scope in Angular?

When registering the Services, it is important to think about the scope, in which you want to provide it. Angular basically offers three scopes:

  1. Root Scope
  2. Module Scope
  3. Component Scope

Where you register the dependency, it defines the lifetime of the dependency as well. When we provide the service in the @ngModule of the root module or any eagerly loaded module, the will be available everywhere in the application.  The Services provided in the @ngModule of the lazy loaded module are available in that module only.

If we provide the services in the @Component, @pipe, or @Directive then they are available only in that component and all of its child components.

1. The Root Scope (Singleton Scope):

The services that we provide at the root module are app-scoped, which means that we can access them from every component and service within the app.

The root scope is the most commonly used scope for providing services as it is also the default scope when creating a Service via Angular CLI. The default providedIn argument in the CLI generated is "root". 

The service here means that the service will be provided in the application root, the AppModule. Therefore the Service will be a singleton, meaning that there will be only one instance of this Service even if it is injected in multiple Modules and used in multiple Components or Directives.

2. The Module Scope:

We can provide the service in the Module scope as well. Any service provided in the other Modules (Other than the Lazy Loaded Module) is also available for the entire application.

The services that are provided in a Lazy Loaded Module are module scoped and available only in the Lazy loaded module.

But what if we don't want to share the service between modules? We can then use the providers array option in the corresponding @NgModule instead of provideIn property.

3. The Component Scope:

We can also create an individual Service instance for a Component by using the Component scope. The services provided at the Component level are available only to the Component & and to the child components.

Q165. What is the difference between the service registered as root vs platform type in the ProvidedIn property of Angular?

The providedIn allows us to specify how Angular should provide the dependency in the service class itself instead of in the Angular Module. It also helps to make the service tree shakable i.e. remove the service from the final bundle if the app does not use it.

ProvidedIn root:

We use the ProvidedIn "root" option when we want to register the application-level singleton service. The root option registers the service in the Root Module Injector of the Module Injector tree. This will make it available to the entire application. This is irrespective of whether the service is lazy-loaded or eagerly loaded. If it is never used it will not be added in the final build (tree shaking).

But remember registering a service in a @NgModule will make it available in that Module only (Singleton within the Module Scope) not application-wide singleton. Using both makes it singleton for the rest of the application, while it creates a separate instance for that Module.

ProvidedIn platform:

It is a special singleton platform injector shared by all applications on the page. The platform allows us to add the service to the Providers of the Platform Injector

The Platform Injector is the parent of the Root Module Injector in the Module Injector tree. This is useful if you have multiple Angular Apps running on a single page. This is a useful option if you are using Angular Elements, where they can share a single instance of service between them.

Root vs Platform:

Platform’ is most likely used for creating shared services for Angular Elements.  ‘Root’ is the default for most services in Angular. All the Services registered as "root" are singleton for the whole application. It makes it very convenient to create tree-shakable services that are singleton within an application.

The difference between ‘root’ and ‘platform’ is only noticeable when running multiple Angular applications in the same window. Both make sure that only one singleton exists even for lazy-loaded modules. But when running two applications in the same window, each application has its own root injector but both share the platform injector.

Q166. What is the difference between a service registered as "root" vs "any" type in the ProvidedIn property of Angular?

The providedIn property allows us to specify how Angular should provide the dependency in the service class itself instead of in the Angular Module. It also helps to make the service tree shakable i.e. remove the service from the final bundle if the app does not use it.

ProvidedIn root:

Root’ is the default for most services in Angular 6. All the services registered as "root" are singleton for the whole application. It makes it very convenient to create tree-shakable services that are singleton within an application. 

In Angular 6 providedIn: "root" property was added to providers, to make services tree-shakable.  Tree shaking is a process to remove, unused code from our application. Angular 9 introduces two new options for @injectable decorator ProvidedIn in addition to the previous root and module options, now we have two additional options platform any.

We use the ProvidedIn "root" option when we want to register the application-level singleton service. The root option registers the service in the Root Module Injector of the Module Injector tree. This will make it available to the entire application. This is irrespective of whether the service is lazy-loaded or eagerly loaded. If it is never used it will not be added in the final build (tree shaking). 

But remember registering a service in a @NgModule will make it available in that Module only singleton (Singleton within the Module Scope), not application-wide singleton. Using both makes it singleton for the rest of the application, while it creates a separate instance for that Module

ProvidedIn any:

We use ProvidedIn "any" when you want every lazy-loaded module to get its own instance of the service. The eagerly loaded modules always share the same instance provided by the Root Module Injector. Hence this will not have any effect on them.

Root vs. Any:

The "root" tells Angular to provide the service in the application root level and the service will be created only once (singleton service ) and provide the same instance in every module that injects the token. 

On the other hand "any" provides a unique instance in every module (including lazy modules) that injects the token, but all eagerly loaded modules will share a singleton instance.  So it's an instance per module injector scope i.e. one instance per runtime bundle. 

Why is the "any" type required?

The problem with working with a lazy loaded module is that if we use providedIn: 'root' even though we think we should get a new instance of a service, it gives the same instance and that might not be the behavior sometimes we expect. If we want each and every lazy-loaded module should have a new instance. When working with a lazy-loaded module, a new instance should be created when we load the module. 

The first way to resolve this issue, you implement forRoot() and forChild() static methods, so each component can have its own instances. The second way to achieve this was to provide the services in each and every module itself so that every module should have its own service instance. But the problem is service is not tree shakable any more.

For all the above problems, the only solution is "any" type. If you set providedIn property to "any", you get the separate instances, without implementing forRoot or forChild static methods or compromising with tree-shakable providers. Now all the eager-loaded modules will share one common instance and all lazy-loaded module will have their own instance of ConfigService.

The ‘any’ type is very helpful to make sure a service is a singleton within module boundaries. It’s a robust alternative to ‘root’ to make sure the individual modules don’t have a side effect on each other.

@Injectable({
  providedIn: 'any'
})
export class UserService { }

Q167. What is the difference between providedIn: root vs forRoot() pattern in Angular?

singleton service is a service for which only one instance exists in an application. If you set the providedIn property of the @Injectable() to "root", it registers the service as a singleton for the whole application. Beginning with Angular 6.0, the preferred way to create a singleton service is to set providedIn to root on the service's @Injectable() decorator. This tells Angular to provide the service in the application root.

When you mark @Injectable as provided in the root, Angular resolver will know that such Injectable, used in the lazy module, was added to the root module, and will look for it in the root injector, not newly created lazy loaded module injector (default behavior).

@Injectable({
  providedIn: 'root'
})
export class UserService { }

On the other hand, forRoot() is a static method in Angular. The forRoot() method is a kind of agreement/convention between Angular developers to call this method in the root module only (AppModule for example), so any service will be provided only once. For this, you should create a module and implement the static forRoot(): ModuleWithProviders method.

@NgModule({
  imports: [CommonModule]
})
export class SettingsModule {
  public static forRoot(): ModuleWithProviders {
  return {
    ngModule: SettingsModule,
    providers: [SettingsService]
  };
  }
}

What if we combine "root" with "forRoot"?

Actually, there is no difference between using forRoot or providedIn or forRoot with providedIn. Service will be created only once. But if you use forRoot or providedIn with root and providers list, the Service will be duplicated.

Q168. What are the different ways to create Singleton Services in Angular?

singleton service is a service for which only one instance exists in an application. There are three ways to make a service a singleton in Angular:

  1. By setting the providedIn property of the @Injectable() to "root"
  2. By registering the services in the AppModule or in a module that is only imported by the AppModule
  3. By forRoot() Pattern

By using providedIn Property of @Injectable() to "root":

Beginning with Angular 6.0, the preferred way to create a singleton service is to set providedIn to root on the service's @Injectable() decorator. This tells Angular to provide the service in the application root. It also makes your services tree-shakable.

@Injectable({
    providedIn: 'root',
  })
  export class UserService {
  }

By using the NgModule providers array:

In applications built with Angular versions prior to 6.0, services are registered NgModule providers array. If the NgModule is the root AppModule, the registered service would be singleton and available throughout the application. 

You can include the service in the root module or in a module that is only imported by the root module. It has been used to register services before Angular 6.0.

@NgModule({
  declarations: [],
  imports: [CommonModule],
  providers :[UserService]
})
export class UserModule{ }

By using forRoot() Pattern:

If a module defines both providers and declarations (components, directives, pipes), then loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton. There are multiple ways to prevent this:

  • Use the providedIn syntax instead of registering the service in the module.
  • Separate your services into their own module.
  • Define forRoot() and forChild() methods in the module.

Use forRoot() to separate providers from a module so you can import that module into the root module with providers and child modules without providers. If you have a module that has both providers and declarations, you can use this technique to separate them out and you may see this pattern in legacy applications. For this just create a static method forRoot() on the module. And then place the providers into the forRoot() method.

static forRoot(config: UserServiceConfig): ModuleWithProviders<GreetingModule> {
  return {
    ngModule: GreetingModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}

Q169. What is an Element Injector Tree in Angular?

An element Injector tree is created for DOM Elements like components and directives when the application starts. Angular creates ElementInjector hierarchies implicitly for each DOM element. Angular creates the Element Injector tree for the services to be provided at the element level

@Directive() vs @Component(): Actually, a component is a special type of directive, which means that just as @Directive() has a providers property, @Component() does too. This means that directives as well as components can configure providers by using the providers property. When you configure a provider for a component or directive using the providers property, that provider belongs to the ElementInjector of that component or directive. Components and directives on the same element share an injector.

The Injector instance of the Root Component becomes the root Injector for the Element Injector tree. It gets the Providers from the provider’s property of the Root Component. The Root Component acts as a parent to every element ( i.e. Component or Directives) we create. Each of those elements can contain child elements creating a tree of elements. The Angular creates an Injector for each of these elements creating a tree of Injectors.

Each Injector gets the list of Providers from the @Directive() or @Component(). If the Providers array is empty, then Angular creates an empty Injector. The Angular will destroy the Injector when Angular destroys the element. Providing service in the @Component() decorator using its providers or viewProviders property configures an ElementInjector.

When you provide services in a component, that service is available by way of the ElementInjector at that component instance. It may also be visible at child components/directives based on visibility rules described in the resolution rules section.

@Component({
  providers: [{ provide: ItemService, useValue: { name: 'lamp' } }]
})
export class TestComponent{}

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 publicProvidersproperties 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.

Q170. What is the Merge Injector in Angular?

Actually, when lazy-loaded modules started to be widely used in Angular, there was a major bug noticed i.e. dependency injection system caused doubled instantiation of lazy-loaded modules. So, starting from that moment we’ve had two parallel trees: one for elements and the other for modules.

When someone asks for some dependency in a component or in a directive angular uses Merge Injector to go through the element injector tree and then, if dependency won’t be found, switch to the module injector tree to resolve the dependency. 

Merge injector can also resolve such built-in things as ElementRefViewContainerRefTemplateRefChangeDetectorRef, etc. Basically, every element can have a merge injector even if you didn’t provide any token on it.

@Directive({
  selector: '[someDir]'
}
export class SomeDirective {
 constructor(private injector: Injector) {}
}

Here, the injector is a merge injector. Similarly, we can inject a Merge injector in a component constructor.



Recommended Articles




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

No comments:

Post a Comment

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