Angular(二) Understanding Dependency Injection for angular
1. The official doc link for denpendency injection
Angularhttps://angular.io/guide/dependency-injection
Denpendency Injection, or DI, is one of fundamental concepts for angular, DI is writed by angular framework and allows classes with Angular decorators, such as Components, directives, Piples and Injectables, to configure dependencies that they need.
Two main roles exists in DI system: dependency consumer and dependency provider.
2. @Injectable
An angular class or other definition that provides a dependency using dependency injection mechanism. An injectable service class must be marked by @Injectable decorator, Other items, such as constant values, can also be injectable.
There has an option for @Injectable decorator ,which is "providedIn?".
Option | Description |
---|---|
providedIn? | Determines which injectors will provide the injectable. |
It mainly has 5 value for diferent scenario:
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null
But "Type<any>" and "any" are DEPRECATED.
- 'null' : equivalent to undefined. The injectable is not provided in any scope automatically and must be added to a providers array of an @NgModule, @Component, or @Directive.
- 'root': The application-level injector in most apps.
- 'platform': A special singleton platform injector shared by all applications on the page.
The following example shows a service class is properly marked by @Injectable so that a supporting service can be injected upon creation.
@Injectable()
class UsefulService {
}
@Injectable()
class NeedsService {
constructor(public service: UsefulService) {}
}
const injector = Injector.create({
providers:
[{provide: NeedsService, deps: [UsefulService]}, {provide: UsefulService, deps: []}]
});
expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true);
3. Providing dependency
A providing dependency must be marked by @Injectable() decorator, let us see the @Injectable decorator.
The first step is to add the @Injectable decorator to show that the class can be injected:
@Injectable()
class HeroService {}
The next step is to make it available in the DI by providing it. A dependency can be provided in multiple places:
- At the component level, using the providers field of the @Component decorator. In this case the HeroService becomes available to all instances of this component and other component and directives used in the template, for example:
@Component({
selector: 'hero-list',
template: '...',
providers: [HeroService]
})
class HeroListComponent {}
When you register a provider at the component level, you will get a new instance of the service witch each new instance of that component.
- At the application level, there has two ways to define a provider. one realized by @NgModule in providers field.
@NgModule({
declarations: [HeroListComponent]
providers: [HeroService]
})
class HeroListModule {}
The other method is realized by @Injectable decorator, we just need to fill the value 'root' in "provideIn" option:
@Injectable({
providedIn: 'root'
})
class HeroService {}
note that this way's suitable for one instance in a project, but if you want to each component has a new instance, the first way is a good choice.
4. Injecting a dependency
There has two ways to inject a dependency in angular, one is we can use constructor, the another option is to use inject method. The constructor can look like this:
@Component({ … })
class HeroListComponent {
constructor(private service: HeroService) {}
}
use inject method:
@Component({ … })
class HeroListComponent {
private service = inject(HeroService);
}
(类似于spring 三级缓存管理bean 的机制)When angular discvoer that a component depends on a service, it first check if the injector has any existing instance of that service, If a requested service instance doesn't yet exit, the injector creates one using the registered provider, and adds it to the injector before returning the service to Angular.
When all requested services have been resolved and returned, Angular can call the component's constructor with those services as arguments.
5. Case
This is my hello component, I want to refer the another component: english
import {Component} from "@angular/core";
@Component({"template":"<div>I love studying</div>"})
export class StudyEnglishComponent{
public vocabulary(){
console.log("for the record, I have finished my vocabulary today!")
}
}
import 'reflect-metadata'
import { Component } from "@angular/core";
import { StudyEnglishComponent} from "../study/study.english.component";
// @MyComponent({
// "selector":'hello-word',
// "template":'<div>hello word</div>',
// })
@Component({'selector':'hello-word',template:'<div class="hello" style="font:200px">hello world</div>',styleUrls:['./helloWord.component.css']})
export class HelloComponent{
public studyEnglish:StudyEnglishComponent;
constructor(english:StudyEnglishComponent){
this.studyEnglish=english;
this.sayHai(this.say);
}
public say="hi angular !";
public sayHai(params:String) {
this.studyEnglish.vocabulary();
return "hi,"+params;
}
}
but I get an error like this: NullInjectorError: R3InjectorError(AppModule)[StudyEnglishComponent -> StudyEnglishComponent -> StudyEnglishComponent]:
NullInjectorError: No provider for StudyEnglishComponent!
How to solve this problem ?
1. add @Injectable decorator on StudyEnglishComponent to declare this class is a provider.
import {Component,Injectable} from "@angular/core";
@Component({"template":"<div>I love studying</div>"})
@Injectable()
export class StudyEnglishComponent{
public vocabulary(){
console.log("for the record, I have finished my vocabulary today!")
}
}
2. Use constructor inject the StudyEnglishComponent
import { Component } from "@angular/core";
import { StudyEnglishComponent} from "../study/study.english.component";
@Component({
selector:'hello-word',
template:'<div class="hello" style="font:200px">hello world</div>',
styleUrls:['./helloWord.component.css'],
providers:[StudyEnglishComponent]
})
export class HelloComponent{
public say="hi angular !";
public studyEnglish:StudyEnglishComponent;
/**
* 使用构造器形式注入
*/
constructor(studyEnglish:StudyEnglishComponent){
this.studyEnglish=studyEnglish;
console.log(this.sayHai(this.say));
}
public sayHai(params:String) {
this.studyEnglish.vocabulary();
return "hi,"+params;
}
}
reboot your serve and see the console:
Problem solved !