Angular2 - Class - InjectMetadata

看来要把Injector 和 provide 一起看了才能理解通注入这块

来自官网的提示

Injectable Services

  • Notice that we imported the Angular Injectable function and applied that function as an @Injectable() decorator.
  • Don’t forget the parentheses! Neglecting them leads to an error that’s difficult to diagnose.
  • TypeScript sees the @Injectable() decorator and emits metadata about our service, metadata that Angular may need to inject other dependencies into this service.
  • The HeroService doesn’t have any dependencies at the moment. Add the decorator anyway. It is a “best practice” to apply the @Injectable() decorator ​from the start​ both for consistency and for future-proofing.

看群主的解释
https://github.com/kittencup/angular2-ama-cn/issues/61

@Injectable() — 表示当前Class可以注入,强制产生metadata,把构造里的类型当token 去找provider 并实例化
如果构造里还有@Inject(‘xx’), 就根据 ‘xx’ 当token来去找provider 实例化,没有就按类型找
等同于 Injector.resolveAndCreate

@Inject(‘token’) — 传入token 找 provider 去实例化

@Inject()

参数是Token

string可以当token,其他type也行

// @Inject(token) to inject the dependency
constructor(@Inject('app.config') private _config: Config){ }

可以用OpaqueToken 产生不重复的token

import {OpaqueToken} from 'angular2/core';
export let APP_CONFIG = new OpaqueToken('app.config');

到底什么是Token

  • 只是一个名字 来代表一个class,当注入这个token的时候会从Injector里找到他并实例化
  • 一个token可以代表一群Class,当注入token的时候 会去找token对应的class(provider) 去实例化

先看对Injector的说明

A dependency injection container used for instantiating objects and resolving dependencies.
An Injector is a replacement for a new operator, which can automatically resolve the constructor dependencies.
In typical use, application code asks for the dependencies in the constructor and they are resolved by the Injector

实例化依赖的容器,一般用构造里的依赖是被Injector处理

Injector.resolve 返回的是 Map<number, ResolvedProvider>

static resolve(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
return resolveProviders(providers);
}

resolveProviders

/**
* Resolve a list of Providers.
*/
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {

var normalized = _normalizeProviders(providers, []); //返回一系列provider数组
var resolved = normalized.map(resolveProvider);
return MapWrapper.values(mergeResolvedProviders(resolved, new Map<number, ResolvedProvider>())); //处理一些是否multi 是否exist等参数返回一个 Map<number, ResolvedProvider>
}

想注入首先要有provider


The provide function

当我们写 provider:[Logger]

This is actually a short-hand expression for a provider registration that creates a new instance of the Provider class.
provider:[provide(Logger, {useClass: Logger})]

The provide function is the more common and friendlier way to create a Provider:
provider:[provide(Logger, {useClass: Logger})]

用provide function 创建 provider

第一个参数是Token

export function provide(token, {useClass, useValue, useExisting, useFactory, deps, multi}: {
useClass?: Type,
useValue?: any,
useExisting?: any,
useFactory?: Function,
deps?: Object[],
multi?: boolean
}): Provider {
return new Provider(token, {
useClass: useClass,
useValue: useValue,
useExisting: useExisting,
useFactory: useFactory,
deps: deps,
multi: multi
});
}

Provider Class

constructor(token, {useClass, useValue, useExisting, useFactory, deps, multi}: {
useClass?: Type,
useValue?: any,
useExisting?: any,
useFactory?: Function,
deps?: Object[],
multi?: boolean
}) {
this.token = token;
this.useClass = useClass;
this.useValue = useValue;
this.useExisting = useExisting;
this.useFactory = useFactory;
this.dependencies = deps;
this._multi = multi;
}
  • provide() — 生成provider
  • Provider — 生成provider
  • Injector — 直接使用Injector 创建查找 resolve 来产生provider //新建的injector

provide function 和 provider Class 用法差不多, 还是Provider,provide方法也是调用的Provider 类
参数神马的等注入继承到时候再看

使用provider

providers:[provide(APP_CONFIG, {useValue: CONFIG})]  //配置provider
constructor(@Inject(APP_CONFIG) private _config: Config){ } //根据token 注入

简单说下继承

组件对对应一个Injector 树

外层Injector可以被里层的组件使用。而且这时候是单例的
而当里层组件也声明了这个provider,这时候两个Injector就是不同的

父组件



@Component({
selector: 'parent-component',
template: `Parent (<child id="child"></child>)(<child #child></child>) <numberList></numberList>
<li> <a [routerLink]="['./NumberList']">NumberList</a></li>
<li> <a [routerLink]="['./NumberItem']" target="_blank">NumberItem</a></li>
<router-outlet></router-outlet>
Parent (<some-component></some-component>) <zippy>hahah houhou</zippy>`,
directives: [numberList, ROUTER_DIRECTIVES, Zippy],
providers:[InjectClassMore,InjectClass]
})
@RouteConfig([
{ path: '/', name: 'NumberList', component: numberList, useAsDefault: true },
{ path: '/numberItem', name: 'NumberItem', component: numberItem }
])
export class ParentApp {

// constructor(dcl: DynamicComponentLoader, elementRef: ElementRef) {
// dcl.loadIntoLocation(ChildComponent, elementRef, 'child');
// }
// constructor(dcl: DynamicComponentLoader, injector: Injector) {
// console.log(dcl.loadAsRoot(ChildComponent, '#child', injector));
// }
viewRef: ViewRef;
constructor(public appViewManager: AppViewManager, compiler: Compiler,public ina:InjectClassMore) {
var xx =6;
compiler.compileInHost(ChildComponent).then(function(hostProtoViewRef: HostViewFactoryRef) {
var a = hostProtoViewRef;
this.viewRef = appViewManager.createRootHostView(hostProtoViewRef, 'some-component', null);
console.log(this.viewRef);
return true;
})
}


// constructor(dcl: DynamicComponentLoader, elementRef: ElementRef) {
// dcl.loadNextToLocation(ChildComponent, elementRef);
// }
}

子组件


@Component({
selector: 'numberList',
template: `
<div *ngFor = "#number of numbers" >
<numberItem [number]="number"></numberItem>
</div>
<input #input />
<button (click)="addNumbers(input.value)">addNumbers</button>
<div>{{moreNumber}}</div>{{haha | date:'medium'}}
`,
directives: [numberItem],
providers:[InjectClassMore]
})
export class numberList {
numbers: Array<number>;
haha = Date.now();
constructor(public ina:InjectClassMore, _inject:Injector) {
ina.a="change a in child com";
console.log("from NUmberlIst",ina);
console.log("from parent",_inject.parent.get(InjectClassMore));

this.numbers = [1, 2, 3, 4];
}

打印结果

from NUmberlIst InjectClassMore {a: “change a in child com”, b: “inject var”}
from parent InjectClassMore {a: “inject var 2”, b: “inject var”}

这符合在boostrap里声明 store (redux)造成的全局效果