13

I have a module in Angular that is structured likes this:

moduleName
    componentA
    componentB

Now componentA and componentB are very similar, as they share some attributes and methods, e.g.:

protected available: boolean = true;

As I don't want to repeat myself, I've created a base class, that stores all this:

export abstract class BaseComponent {
    protected available: boolean = true;
}

And both controllers inherit from that class:

import { BaseComponent } from '../base.component';

export class ComponentA extends BaseComponent implements OnInit {
    constructor() {
        super();
    }

    ngOnInit() {
        console.log(this.available);
    }
}

This works just fine. However, when I research this soultion a lot of people are saying:

Don't use inheritance, use composition in this case.

Alright, but how can I use composition instead? And is the gain really that big over the current solution?

Thanks a lot for your time.

  • 3
    i don't know what composition is, but when there is a lot of repeated code in 2 components i would use a service instead – Hussein Sep 14 '18 at 14:02
  • 2
    @Hussein Composition is defined as *containing instances of other classes that implement the desired functionality*. Your suggestion to use a service is perfect for this. – Kirk Larkin Sep 14 '18 at 14:05
  • 1
    @Hussein primer on composition - say you have some validation logic. You separate it in a new class called Validator. Now you can just add `validator = new Validator()` and later do `validator.check(input)` in many classes. As opposed to each class extending `Validator` and inheriting the `check()` functionality. – VLAZ Sep 14 '18 at 14:16
  • 1
    Ah I see. So, if I create a service and inject it into my component, I have to use `serviceName.available` to access a property. But the advantage is, that the service is coupled more loosely, right? And a service is not necessarily global in Angular - I mean: the injected services are all individual instances for their component, correct? @KirkLarkin – someone.else.2020 Sep 14 '18 at 14:18
  • 1
    In Angular, it depends on how you *register* the service. It can be registered with the root and therefore be a singleton; otherwise it can be registered with a component and be unique per component instance ([docs](https://angular.io/guide/hierarchical-dependency-injection#where-to-configure-providers)). – Kirk Larkin Sep 14 '18 at 14:20
  • Thanks @vlaz I was going to add something similar as an explanation. – someone.else.2020 Sep 14 '18 at 14:20
  • @KirkLarkin Ah I see, thank you. – someone.else.2020 Sep 14 '18 at 14:24
  • @vlaz thanks for the explanation,i will have to dig more sometime – Hussein Sep 15 '18 at 00:00

3 Answers3

10

For composing objects in angular you need to have a reference to that object inside of your class, which shares data and functionality. To do that you need to use Angular services, and inject them to your class, and there should be 1 instance of service per component.

  1. Create a new service by running ng g s my-service, remove providedIn: 'root' from your service annotation (We want to provide instance per component)
  2. Add public available: boolean = true; to the service
  3. provide the service through the components, in @Component configs on your components
  4. inject the service in your both component constructors, constructor(private myService:MyService)

Now you have a composition that keeps data and functionality

@Component({
  selector: 'app',
  templateUrl: './app.my-component.html',
  styleUrls: ['./app.my-component.css'],
  providers: [MyService]
})
export class MyComponent {
  constructor(private myService: MyService) {
  }
}
Reza
  • 18,865
  • 13
  • 88
  • 163
  • I think I like this approach better than inheritance in this case. Thanks. It seems that I can't upvote your answer though. – someone.else.2020 Sep 14 '18 at 14:52
  • @someone.else.2020 no worries, glad it helped – Reza Sep 14 '18 at 17:46
  • 1
    @someone.else.2020 Just pay attention to step 3, you need to provide service to component level, so that you will get a new instance of service for each instance of component – Reza Sep 14 '18 at 17:47
  • I would personally suggest using an interface that abstracts the logic of each component. If the parameters and return type are added to the interface method, then this abstracts the logic away and avoids using inheritance. – jburtondev Dec 21 '19 at 00:21
3

If you create same components with big part same logic. you can use inheritance for example controlSelectComponent and controlInputComponent stackblitz example

For composition you need to create service and provide it to both components. But you dont keep component state in service becose all service are singletone. And when one component change state another component crash.

You also can provide service to each component in providers section

@Component({
  selector: 'app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [MyService]
})
export class AppComponent {
  constructor(private myService: MyService) {
  }
}

But in case with saving state in service is not the best solution

Conclusion

Use services and composition for share helper methods between components.

Use abstract class and inheritance for components with same logic and state changes.

Kliment Ru
  • 2,057
  • 13
  • 16
  • 3
    Are you sure that this service will be singleton? This is from the manual: "*When you register a provider at the component level, you get a new instance of the service with each new instance of that component.*". I understand that the scope of the service depends where you declare it (root singleton throughout the app, module one instance for all components, component like quoted), right? – someone.else.2020 Sep 14 '18 at 14:33
  • I meant this. By default you provide service in module and service generate one instance for module. When you provide it to component you have service instance for each component. Look at my example `providers: [MyService]` – Kliment Ru Sep 14 '18 at 14:37
0

I would also recommend to read about Composition over Inheritance. The syntax(InversifyJs) is very similar that Angular uses. Please see this blog

Vugar Abdullayev
  • 1,852
  • 3
  • 21
  • 46
  • 1
    Kindly add context to any links so your Answer is self contained, meaning the answer needs to be here in the Answer itself. See ["Provide context for links"](https://stackoverflow.com/help/how-to-answer). It would be preferable if you could answer the Question in your own words here and link only as a reference. – Scratte Aug 18 '21 at 21:19