I already know what/how to use deps
.
It is when a factory method needs Injected tokens , so we need to supply them like :
const randomFactory = (car,engine) => { return ... };
...
providers: [Car,Engine,
{ provide: 'Random',
useFactory: randomFactory ,
deps: [Car, Engine],
},
]
But I've read here :
so it's basically
deps
only relevant whenuseFactory
is used, correct?
->Exactly - only foruseFactory
But then I've asked in other place :
Can deps be used with useClass ? I thought they are only for useFactory –
-> Yes they can. It would be useful when you’re injecting generic dependencies that require explicitly named tokens
I didn't want to continue comments in two places and hence my question :
Question:
In which scenarios would I use useClass
with deps
?
Also , even if I used it , say class Foo
:
Class Foo
{
constructor ( private s1:Service1 , private s2:service2){}
}
^ Which already(!) have a ctor of its own. Where would deps dependencies be injected ? ( appended to ctor??)
}
An example of scenario + code would be much appreciated.
The deps property is an array of provider tokens. The Logger and UserService classes serve as tokens for their own class providers. The injector resolves these tokens and injects the corresponding services into the matching heroServiceFactory factory function parameters.
To summarize, choose useValue to provide the same injected value each time*, useFactory when the injection value should be computed at runtime, and useClass when you want the injector to construct a value of Type<any> to provide.
Class Provider: useClassThe useClass expects us to provide a type. The Injector creates a new instance from the type and injects it. It is similar to calling the new operator and returning instance. If the type requires any constructor parameters, the injector will resolve that also.
With useFactory we can use a factory at runtime to decide which kind of service we want to return if it got requested by any other class in our application. Note that you do not want to change the method and/or property calls on your requesting instances when ServiceB is being returned instead of ServiceA .
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[].
Changelog
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
:)
Yes, deps
may be used by useFactory
or useClass
.
You can see that this is true by looking at the Angular source (5+) for packages\compiler\src\metadata_resolver.ts. If deps
is populated for either useClass
or useFactory
, then deps
will be returned in the provider metadata:
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
};
}
You can see from packages\compiler\src\view_compiler\provider_compiler.ts that deps
is used to instantiate a multi provider:
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***
}
The same is true for single providers (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***
}
So even though it is not well documented, deps
can be used by useClass
or useFactory
.
As an aside, deps
is ignored when using useExisting
or useValue
providers (https://github.com/angular/angular/blob/5.2.x/packages/compiler/src/view_compiler/provider_compiler.ts#L108):
} 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 = [];
}
That being said, in the typical case, having a useClass
provider and explicitly naming dependencies in the deps
array is usually not necessary. You should let DI handle that for you implicitly.
I found some obscure use cases for it when trying to implement a static forRoot
method as referenced by these questions here and here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With