The concept of dependency injection in Angular starts with:

Service Service

The representation of a Service is a class that can be reused in a component. For example, Http requests to retrieve data, log processing, and validating user input are written as services for the component to use.

import { Injectable } from '@angular/core'; // In Angular, to define a class as a service, we provide the metadata @Injectable({// we) with the '@Injectable' decoratordeclare that this service should be created
  // by the root application injector.
  providedIn: 'root',})export class LoggerService {
  warn(msg) { 
    returnconsole.warn(msg); }}Copy the code

Injector Injector

Instead of doing it yourself, Angular creates an application-wide injector and any other injectors you need during startup.

The Provider Provider

Is an object that tells the Injector how it should acquire or create dependencies.

Open Angular and see the following code snippet, app.module.ts

@NgModule({ declarations: [ .... ] , imports: [ .... ] // Providers tell Angular which objects to inject. // Providers are arrays, each of which is provider providers: [// abbreviated, equivalent {provider: LoggerService, useClass: LoggerService} LoggerService, { provide: RequestCache, useClass: RequestCacheWithLocalStorage }, { provide: HTTP_INTERCEPTORS, useClass: UrlInterceptor, multi:true
    },
  ],
  bootstrap: [AppComponent]
})
Copy the code

DI token

The provide attribute provides a provider token, also called a token, that represents the type specified in the constructor. When constructor(private productService: productService){… } select * from provider where token is ProductService;

Several ways to write Provider
  1. useClass providers: [{provide: ProductService, useClass: ProductService} ]The shorthand isproviders: [ ProductService ]The useClass attribute specifies the instantiation mode, indicating that it is a new ProductService ifuserClass" AnotherProductServiceThe actual instantiation is AnotherProductService.
  2. In addition to useClass, userFactory can also be written using the userFactory factory method, which returns an instance as the contents of the productService parameter in the constructor.providers: [{provide: ProductService, userFactory: () => {}} ]This allows you to instantiate objects based on conditions, which is more flexible
providers: [{
  provide: ProductService, 
  userFactory: () => {
    let logger = new LoggerService();
    letDev = math.random () > 0.5;if (dev) {
      return new ProductService(logger);
    } else {
      return new AnotherProductService(logger);
   }
  }
}, LoggerService ]
Copy the code

One drawback of the above notation is that LoggerService and ProductService are too strongly coupled. Further optimization uses the DEps parameter, which refers to the parameter on which the factory declaration depends.

providers: [{
    provide: ProductService, 
    userFactory: (logger: LoggerService) => {
      letDev = math.random () > 0.5;if (dev) {
        return new ProductService(logger);
      } else {
        return new AnotherProductService(logger);
     }
    },
    deps: [ LoggerService ]
  }, 
  LoggerService
]
Copy the code

Optimize again and define a third provider with the token being IS_DEV_ENV and the value being the concrete false

Providers: [{provide: ProductService, // provide: ProductService userFactory: (logger: LoggerService, isDev) => {if (isDev) {
        return new ProductService(logger);
      } else {
        return new AnotherProductService(logger);
     }
    },
    deps: [ LoggerService, 'IS_DEV_ENV' ]
  }, 
  LoggerService,
  {provide: 'IS_DEV_ENV', useValue: false}]Copy the code

In general, you can create a provider of type object for injection

Providers: [{provide: ProductService, // Provide: ProductService userFactory: (logger: LoggerService, appConfig) => {if (appConfig.isDev) {
        return new ProductService(logger);
      } else {
        return new AnotherProductService(logger);
     }
    },
    deps: [ LoggerService, 'APP_CONFIG' ]
  }, 
  LoggerService,
  { provide: 'APP_CONFIG', useValue: {isDev: false}}]Copy the code

Scope of the provider

If the provide declaration is in the App module, it is visible to all modules

Provide is a declaration in a component that is visible only to that component and its children. Other components cannot be injected. When a provider declared in a component and a module has the same token, the provider declared in the component overrides the provider in the module.

@ Injectable decorator

Indicates that FooService can be injected into other services via the constructor. For example, if commented out

// @Injectable({
//   providedIn: 'root'
// })
Copy the code

Would be an error

Why can we inject Service directly without @Injectable in the component? We know that we define components with the @Component decorator, pipes with the @Pipe decorator, and they are all subclasses of Injectable. Component is also a subclass of Directive, so all components are directives.

Manual injection

import { Component, OnInit, Injector } from '@angular/core';
import { LoggerService } from '.. /_service/logger.service';

@Component({
  selector: 'app-di',
  templateUrl: './di.component.html',
  styleUrls: ['./di.component.styl']})exportclass DIComponent implements OnInit { logger: LoggerService; Constructor (private injector: injector) {this.logger = injector.get(LoggerService); constructor(private injector: injector) {this.logger = injector.get(LoggerService); }ngOnInit() {
    this.logger.log()
  }
}

Copy the code