41

I have a parent service which has some dependencies like

@Injectable()
export class ParentService{
  constructor(private http:Http, private customService:CustomService){}
}

and I want to extend the service

@Injectable()
export class ChildService extends ParentService{
  constructor (){
    super(??) <= typescript now asking to enter two parameters according to ParentServie's constructor
  }
}

Edit-----------------

@Injectable()
export class ParentService{
  constructor(private http:Http, private customService:CustomService){}
  get(){this.http.get(...)}
}

@Injectable()
export class ChildService extends ParentService{
  constructor (private http:Http, private customService:CustomService){
    super(http, customService)
  }
}

Then I can use in components?

export class Cmp {
  constructor(private childService:ChildService){
    this.childService.get()
  }
}
Han Che
  • 8,239
  • 19
  • 70
  • 116

3 Answers3

44

The parameters of the super class need to be repeated and passed to the super call:

@Injectable()
export class ChildService extends ParentService{
  constructor (http:Http, customService:CustomService){
    super(http, customService);
  }
}

There are some "hacks" to work around like Inheritance and dependency injection

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • no, the constructor misses the parameters. Angular2 DI reads from the constructor parameters what dependencies it has to pass to a component. `ChildService` won't get passed anything in because it doesn't have constructor parameters. `super(http, customService)` is invalid because `http` and `customService` are not known in the sub class. – Günter Zöchbauer Aug 29 '16 at 14:36
  • sorry forgot to add the paramenters to child components constructor. no correct? – Han Che Aug 29 '16 at 14:39
  • This should work. It's similar to what I showed in my answer but now there are also overridden fields `http` and `customService` in the `ChildService` as well. – Günter Zöchbauer Aug 29 '16 at 14:42
  • 1
    Hey, how aboust skipping `private` keyword in `ChildService` constructor? `super()` shall store them as properties – Tomas May 25 '18 at 07:01
  • I can't get this to work without the `ParentService` not being `@Injectable`. Is that correct? Can't tell from the example, which seems to suggest that the `ParentService` can be `@Injectable` – mtpultz Jan 21 '19 at 06:13
  • I wozld expect `@Injectable() on `ParentService` or not to not matter, but havenßt tried. – Günter Zöchbauer Jan 21 '19 at 06:18
24

Simply make base service... non-@Injectable()! For example, you need to supply a method to retrieve user profile in LoginService. This method will be different for different instances of LoginService, and parent class must not know anything about where from this method came: it can be lambda, it can be exported function, it can be a method of another service. To achieve this, you can declare parent service which Parent service:

// Don't annotate it with @Injectable() ! It's a simple class.
export abstract class BaseLoginService {
  constructor(
    // This won't be injected automatically, 
    // child class must call super() and provide value for this argument.
    // Note: it's private, can't be accessed outside of BaseLoginService class
    private http: HttpClient,
    // Method used to retrieve user profile
    private profileGetter: () => Observable<UserDetails>, 
    private router: Router
  ) {
    this.profileGetter().subscribe(); // Do something with this method
  }

Then extend it in child class:

// Child class must be annotated with @Injectable(),
// if you don't need to extend it further
@Injectable() 
export class LoginService extends BaseLoginService {

  constructor(
    // Note: no public/private/protected here - "http"
    // will be just an argument
    http: HttpClient, 
    private profileService: ProfileService, // This is the private field of LoginService class
    router: Router,
    // Some service used by LoginService class, parent class BaseLoginService
    // doesn't need to know about SomeOtherService
    private someOtherService: SomeOtherService
  ) {
    super(
      http,
      // Need lambda here to capture profileService instance. If
      // profileService.getUserDetailsMethod doesn't use instance
      // fields or methods, then we can simply write 
      // "profileService.getUserDetailsMethod" (without quotes, obviously).
      () => profileService.getUserDetailsMethod(), 
      router
    );
    this.someOtherService.doSomething(); // Custom initialization code
  }

Note: in providers section of module specify LoginService instead of parent BaseLoginService:

providers: [
  LoginService,

and use it in component classes:

export class LoginComponent implements OnInit {
  constructor(
    private loginService: LoginService
  ) {
  }

If you need to use parent service (e.g. in shared components which only need functionality from parent service class), then provide BaseLoginService this way:

providers: [
  {provide: BaseLoginService, useExisting: LoginService}, // For those components which need functionality from base class only
  LoginService, // Still need this one for those components which need functionality from child class
izogfif
  • 6,000
  • 2
  • 35
  • 25
  • I know it depends, but say you have N isolated entities in your app and they have to implement common methods. In your opinion, what is the recommended directory/place to store such classes? – Andrei Gătej Jul 01 '19 at 09:50
  • @AndreiGătej I would suggest to start from asking yourself: why do you have such a question? Is there a code style you have to adhere to, but this aspect is not covered by it? If you're not working on this project alone, ask your project manager. In any way, if you are using some IDE, you can rename the directory later and IDE will automatically adjust all paths / includes in all places. Personally, I stored those base classes in the separate Angular project which was the shared dependency for others. – izogfif Jul 01 '19 at 10:19
4

If you make the DI services in the abstract class protected and omit the constructor in the subclass, then the services are available in the subclass.

Joey Gough
  • 2,753
  • 2
  • 21
  • 42