Angular containers and inheritance

Angular containers and inheritance

I used to pass services to constructor of the component in the following way:

import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class MyComponent {
  constructor(
    private myServiceA:MyServiceA, 
    private myServiceB:MyServiceB,
    private myServiceC:MyServiceC) {}
}

That’s fine, but when your app grows you might consider a different approach that makes your architecture more scalable. Let’s create a base component, or so called container that will contain global Injector. This file could be named featureA.base.container.ts

As the name suggests, this component will serve as a base for other components. As in OOP (Object Oriented Programming) we can have for example an Animal class with the properties like Age and Fur and other classes (Dog, Cat) would inherit those properties.

angular-component-inheritence-diagram.png Source: 4 Reasons to Fall in Love with Angular Component Inheritance

Unlike the first snippet where we pass the services as parameters to constructor we can import Injector from Angular core and pass just that. Then using get method we can retrieve the instances of other services. Here’s how it looks like (adjusted snippet from: Inheritance and dependency injection)

// featureA.base.container.ts
import {Injector} from '@angular/core';
import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class BaseComponent {
  protected myServiceA:MyServiceA;
  protected myServiceB:MyServiceB;
  protected myServiceC:MyServiceC;
  sharedProperty: boolean;
  constructor(injector: Injector) {
    this.settingsServiceA = injector.get(MyServiceA);
    this.settingsServiceB = injector.get(MyServiceB);
    this.settingsServiceB = injector.get(MyServiceC);
  }
}

And here’s how to use it in a child component. We have to extend the components that would use those services. Remember, as displayed in the diagram above, it’s also about sharing the properties and methods.

export MyComponent extends BaseComponent {
  constructor(injector: Injector) {
    super(injector);

    this.myServiceA.JustCallSomeMethod();
    this.myServiceB.JustCallAnotherMethod();
    this.myServiceC.JustOneMoreMethod();
    this.sharedProperty = true;
  }
}

Again we use the Injector and super() keyword that calls the parent’s constructor.

The super keyword is used to access properties on an object literal or class’s [[Prototype]], or invoke a superclass’s constructor.

Initially it looks like there’s more things to define, but if you have many services and some properties you would like to share across the components, you can simply inherit them and I think it’s still pretty legible and universal solution. Of course you can go deeper and create several layers of abstraction. The base container we created might extend for example CommonComponent etc.

There’s a similiar pattern described in Designing Angular architecture — Container-Presentation pattern, where Input and Output decorators are used to pass data between the components. Regardless of your pattern choice I hope you found this article useful.


cover image: Photo by Joshua Fuller on Unsplash