我已经知道如何使用deps
当工厂方法需要注入令牌时,我们需要提供它们:
const randomFactory = (car,engine) => { return ... };
...
providers: [Car,Engine,
{ provide: 'Random',
useFactory: randomFactory ,
deps: [Car, Engine],
},
]
但我已阅读here:
所以基本上
deps
仅在使用useFactory
时才相关,是否正确?
- >完全 - 仅适用于useFactory
但后来我问in other place:
deps可以和useClass一起使用吗?我以为它们只是用于工厂 -
时,它会很有用
- >是的他们可以。当您注入需要显式命名的标记的通用依赖项
我不想在两个地方继续评论,因此我的问题是:
问题:
我会在哪些情况下使用useClass
与deps
?
此外,即使我使用它,请说课Foo
:
Class Foo
{
constructor ( private s1:Service1 , private s2:service2){}
}
^ 已经(!)拥有自己的ctor 。 deps依赖注入的位置在哪里? (附加到ctor ??)
}
非常感谢场景+代码的一个例子。
答案 0 :(得分:17)
There are two kinds of providers:
StaticProvider and Provider
It's kind of providers that are used to configure Injector in a static way(without Reflection)
According to the commit
platformXXXX() no longer accepts providers which depend on reflection. Specifically the method signature went from Provider[] to StaticProvider[].
What does it mean?
1) When we pass provider to platform we have to specify deps
because we have to use StaticClassProvider
or ConstructorProvider
instead of just ClassProvider
(see picture above)
platformBrowserDynamic().bootstrapModule(AppModule,
{
providers: [
{
provide: ElementSchemaRegistry,
useClass: CustomDomElementSchemaRegistry,
deps: [] <===================== required here
}
]
}
);
2) When we create Injector dynamically we have to specify deps
because Injector.create
takes StatisProvider
array.
For instance:
export const MyParams = new InjectionToken<string[]>('params');
export class MyService {
constructor(@Inject(MyParams) public someParameters: string[]) {}
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'Angular ' + VERSION.full;
constructor() {
const inj = Injector.create([
{ provide: MyService, useClass: MyService } <=== we will get an error because we have to define deps
])
}
}
https://ng-run.com/edit/5Xm4jwAoXXyAIspwF571
It's kind of providers that we usually use when write providers in @NgModule or @Component/@Directive metadatas
Looking at this answer how the parameters of a forRoot() module's method is passed to a provider? I would say that deps
is not required there. We only need to provide Params
in providers array and angular will do all job for us.
@estus said that
deps are available only in useFactory providers but not in useClass providers.
because he meant Provider
(more precisely ClassProvider
) not StaticProvider
P.S. You can also read my article about StaticInjector
:)
答案 1 :(得分:2)
是的,deps
或useFactory
可能会使用useClass
。
通过查看packages\compiler\src\metadata_resolver.ts的Angular源(5+),您可以看到这是真的。如果为deps
或useClass
填充useFactory
,则会在提供商元数据中返回deps
:
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
let compileDeps: cpl.CompileDiDependencyMetadata[] = undefined !;
let compileTypeMetadata: cpl.CompileTypeMetadata = null !;
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null !;
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
if (provider.useClass) {
compileTypeMetadata = this._getInjectableMetadata(provider.useClass, provider.dependencies);
compileDeps = compileTypeMetadata.diDeps; <-- ***HERE***
if (provider.token === provider.useClass) {
// use the compileTypeMetadata as it contains information about lifecycleHooks...
token = {identifier: compileTypeMetadata};
}
} else if (provider.useFactory) {
compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
compileDeps = compileFactoryMetadata.diDeps; <-- ***HERE***
}
return {
token: token,
useClass: compileTypeMetadata,
useValue: provider.useValue,
useFactory: compileFactoryMetadata,
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : undefined,
deps: compileDeps, <-- ***HERE ***
multi: provider.multi
};
}
您可以从packages\compiler\src\view_compiler\provider_compiler.ts看到deps
用于实例化多提供商:
if (provider.useClass) {
const depExprs = convertDeps(providerIndex, provider.deps || provider.useClass.diDeps);
expr = ctx.importExpr(provider.useClass.reference).instantiate(depExprs); <-- ***HERE***
} else if (provider.useFactory) {
const depExprs = convertDeps(providerIndex, provider.deps || provider.useFactory.diDeps);
expr = ctx.importExpr(provider.useFactory.reference).callFn(depExprs);<-- ***HERE***
}
单个提供商(https://github.com/angular/angular/blob/5.2.x/packages/compiler/src/view_compiler/provider_compiler.ts#L89)也是如此。
if (providerMeta.useClass) {
providerExpr = ctx.importExpr(providerMeta.useClass.reference);
flags |= NodeFlags.TypeClassProvider;
deps = providerMeta.deps || providerMeta.useClass.diDeps; <-- ***HERE***
} else if (providerMeta.useFactory) {
providerExpr = ctx.importExpr(providerMeta.useFactory.reference);
flags |= NodeFlags.TypeFactoryProvider;
deps = providerMeta.deps || providerMeta.useFactory.diDeps; <-- ***HERE***
}
因此即使没有详细记录,deps
或useClass
也可以使用useFactory
。
另外,使用deps
或useExisting
提供商(https://github.com/angular/angular/blob/5.2.x/packages/compiler/src/view_compiler/provider_compiler.ts#L108)时会忽略useValue
:
} else if (providerMeta.useExisting) {
providerExpr = o.NULL_EXPR;
flags |= NodeFlags.TypeUseExistingProvider;
deps = [{token: providerMeta.useExisting}];
} else {
providerExpr = convertValueToOutputAst(ctx, providerMeta.useValue);
flags |= NodeFlags.TypeValueProvider;
deps = [];
}
话虽如此,在典型情况下,通常不需要拥有useClass
提供程序并在deps
数组中明确命名依赖项。你应该让DI隐含地为你处理。
答案 2 :(得分:1)
deps
are available only in useFactory
providers but not in useClass
providers.
That's because the method that was chosen by Angular team for DI annotation as preferable (emitted type metadata) is applicable only to class constructors. While regular functions that are used in useFactory
providers can't make use of type metadata and need alternative annotation method, which is dep
array.
As explained in this answer, classes can be alternatively annotated for DI with parameters
static property. Both class provider parameters
and factory provider deps
accept an array consisting of provider tokens or arrays of decorator instances (like [new Optional(), new Inject(...)]
).