Specify providers at service in Angular 2

2020-03-01 19:26发布

问题:

I'm trying to use Angular 2's DI system to automatically handle my services' dependencies. I'd like to use an annotation on the service itself, rather than using the second parameter of bootstrap() to specify all injectable services.

What I've Got

A low-level service:

services/role-store.ts

export class RoleStore {
  constructor() {
    // Initialize roles
  }

  getById( id ) {
    // accepts id, returns role object
  }
};

A high-level service that depends on the low-level service:

services/user-store.ts

import {Injectable} from 'angular2/angular2';
import {RoleStore} from './role-store.ts';

@Injectable()
export class UserStore {
  constructor( roleStore: RoleStore ) {
    this.roleStore = roleStore;
    // Initialize users
  }

  roleForUser( user ) {
    let role = this.roleStore.getById( user.roleId );
    return role;
  }
};

A component that depends on the high-level service:

components/user-dir.ts

import {Component, View, CORE_DIRECTIVES} from 'angular2/angular2';

import {UserStore} from '../services/user-store';

@Component({
  selector: 'user-dir',
  bindings: [UserStore]
})
@View({
  template: '<!-- inline template -->',
  directives: [CORE_DIRECTIVES]
})
export class UserDir {
  constructor( data: UserStore ) {
    this.userStore
  }
  // other methods...
}

A root component to bootstrap:

app.ts

import {Component, View, bootstrap} from 'angular2/angular2';

import {RoleStore} from './services/role-store';
import {UserDir} from './components/user-dir';

@Component({
  selector: 'app'
})
@View({
  template: '<user-dir></user-dir>',
  styleUrls: ['./app.css'],
  directives: [UserDir]
})
class App {}

bootstrap( App, [RoleStore] );

The Problem

bootstrap( App, [RoleStore] ) works, but I'd rather have an annotation in user-store.ts that tells Angular to inject RoleStore.

Something like @Provide( RoleStore ) class UserStore {}.

Any advice?

回答1:

providers on services are not supported https://github.com/angular/angular/issues/5622

What you can do instead is creating arrays of providers that are exported like "modules"

export const ROLE_STORE_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
    [RoleStore];

and then in modules that use RoleStore service

export const PARENT_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
    [ParentService, RoleStore];

and then use it in bootstrap

bootstrap(AppComponent, [PARENT_PROVIDERS]);

I admit that is not what you asked for but the closest I found so far.



回答2:

You always have to register providers on some level, in your case it would make more sense to register them on the component level so that the dependency is specified there and not on the whole application level. Something like this...

@Component({
  selector: 'user-dir',
  template: '<!-- inline template -->',
  directives: [CORE_DIRECTIVES]
  providers:  [UserStore, RoleStore]
})
export class UserDir {
  constructor( data: UserStore ) {
    this.userStore
  }
  // other methods...
}

This is the official way of doing things that the dependencies are manually registered into Angular 2 DI mechanism on the most specific level possible.

That being said, you would need to write your own compile time / runtime (init) tool which would traverse code collect all @Injectable services and register them into Angular 2 context for you (by using providers)



标签: angular