Search code examples
angulardependency-injectionangular-services

How do I provide single instance of service for component and all content children?


Is there a way I can provide my component-scoped service to all of its content children?

For context, I am building out a table component that contains multiple components and directives that are injected via <ng-content>.

I am providing the service to the component like this:

@Component({
  providers: [MyService]
})
export class Table {
  ...

  constructor(private _service: MyService) {}
}

If I have another component, say:

@Component({

})
export class HeaderCell {
  ...

  constructor(private _service: MyService) {}
}

How can I design this in a way that the HeaderCell component receives the same instance of MyService that is used by the top level Table component?

Do I have to pass the instance via @Input() to the ContentChildren?

What are my options?


Solution

  • It is very easy. Here is our

    HTML

    <div>START</div>
    <hr />
    <app-parent><app-child></app-child></app-parent>
    <hr />
    

    Parent Component

    import { Component, OnInit } from '@angular/core';
    import { TestService } from '../test.service';
    
    @Component({
      selector: 'app-parent',
      templateUrl: './parent.component.html',
      styleUrls: ['./parent.component.css'],
      providers: [TestService]
    })
    export class ParentComponent implements OnInit {
    
      constructor(private test: TestService) { }
      testValue: string = ""
      ngOnInit() {
        this.test.message = "From Parent";
        this.testValue = this.test.message;
      }
    }
    
    // HTML
    <div>{{testValue}}</div>
    <ng-content></ng-content>
    

    The Child Component

    import { Component, Host, OnInit } from '@angular/core';
    import { TestService } from '../../test.service';
    
    @Component({
      selector: 'app-child',
      templateUrl: './child.component.html',
      styleUrls: ['./child.component.css'],
      //providers: [TestService]
    })
    export class ChildComponent implements OnInit {
      //constructor(@Host() private test: TestService) { }
      constructor(private test: TestService) { }
      testValue: string = '';
      ngOnInit() {
        //this.test.message = "From Child"
        this.testValue = this.test.message
      }
    }
    
    // HTML
    <div>CHILD {{testValue}}</div>
    

    The Service is very small:

    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class TestService {
    
      constructor() { }
    
      message: string = "Init"
    }
    

    The @Host() helps to get the Service (or anything) from the parent directly. But is it a Service you don't need it. Is the service provided in the Parent then the Child have access to it, too - with the same instance! We set in the Parent the message value to From Parent and so looks the result:

    enter image description here

    You see the child show From Parent, too. Come from the same service instance.