Angular DI and inheritance: injecting extensions o

2019-01-20 16:45发布

I have a base service and two inhering services:

@Injectable({ providedIn: 'root' })
export class BaseService {
  foo(src?: string){
    return `speaking from ${src || 'BaseService'}`;
  }
}


@Injectable({ providedIn: 'root' })
export class SomeService extends BaseService {
  foo(){
    return super.foo('SomeService')
  }
}

@Injectable({ providedIn: 'root' })
export class AnotherService extends BaseService {
  foo(){
    return super.foo('AnotherService')
  }
}

I wish to inject them in some component and retrieve instances of the three separate classes:

@Component({
    selector: 'my-app',
    template: `
        <div>
            <p>Who's there?</p>
            <p>{{ base }}</p>
            <p>{{ some }}</p>
            <p>{{ another }}</p>

        </div>
    `,
})
export class App {
    base: string;
    some: string;
    another: string;

    constructor(base: BaseService, some: SomeService, another: AnotherService) {
        this.base = base.foo();
        this.some = some.foo();
        this.another = another.foo();

    }
}

Instead, I get three instances of the same class (HTML output):

Who's there?

speaking from BaseService

speaking from BaseService

speaking from BaseService
  • Why doesn't this work?
  • Why are SomeService, AnotherService and BaseService not unique tokens for Angular DI?

It seems that putting

...    
{ provide: SomeService , useClass: SomeService },
{ provide: AnotherService , useClass: AnotherService },
...

in the providers will make it work.

  • Why is this explicitly needed?

A plnkr: https://next.plnkr.co/edit/BvmppLHRbFbz9CFZ

1条回答
叼着烟拽天下
2楼-- · 2019-01-20 17:37

SomeService and AnotherService inherit the decorator metadata from BaseService, so angular injects an instance of BaseService in their place.

This is dangerous, as calling any instance member from either SomeService or AnotherService which isnt inherited from BaseService will trigger a run-time error.

The simplest way to archive the behavior you are looking for, would be to inherit from a common abstract base class, with no decorator:

export abstract class AbstractBaseService {
  foo(src?: string) {
    return `speaking from ${src || 'AbstractBaseService'}`;
  }
}

@Injectable({ providedIn: 'root' })
export class BaseService extends AbstractBaseService {
  foo() {
    return super.foo('BaseService');
  }
}

@Injectable({ providedIn: 'root'})
export class SomeService extends AbstractBaseService {
  foo() {
    return super.foo('SomeService');
  }
}

@Injectable({ providedIn: 'root' })
export class AnotherService extends AbstractBaseService {
  foo() {
    return super.foo('AnotherService');
  }
}

I modified your plnkr to test this approach.

查看更多
登录 后发表回答