可以`deps:[]`也可以和`useClass`一起使用?

时间:2018-02-03 07:16:19

标签: javascript angular

我已经知道如何使用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一起使用吗?我以为它们只是用于工厂 -
   - >是的他们可以。当您注入需要显式命名的标记的通用依赖项

时,它会很有用

我不想在两个地方继续评论,因此我的问题是:

问题:

我会在哪些情况下使用useClassdeps

此外,即使我使用它,请说课Foo

Class Foo
{
 constructor ( private s1:Service1 , private s2:service2){}
}

^ 已经(!)拥有自己的ctor 。 deps依赖注入的位置在哪里? (附加到ctor ??)

}

非常感谢场景+代码的一个例子。

3 个答案:

答案 0 :(得分:17)

There are two kinds of providers:

StaticProvider and Provider

enter image description here

StaticProvider

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

Provider

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)

是的,depsuseFactory可能会使用useClass

通过查看packages\compiler\src\metadata_resolver.ts的Angular源(5+),您可以看到这是真的。如果为depsuseClass填充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***
}

因此即使没有详细记录,depsuseClass也可以使用useFactory

另外,使用depsuseExisting提供商(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隐含地为你处理。

在尝试实现这些问题herehere所引用的静态forRoot方法时,我发现了一些模糊的用例。

答案 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(...)]).