Sam
Sam

Reputation: 1861

Is it possible to inject a service into a child component?

I have a situation where I find myself replicating logic in parent components to fetch data from a server to populate a dropdown (this also includes handling pagination and search). I would therefore like to move this logic to the dropdown component where I can define it once.

However, this requires me defining which service the dropdown component should be using to fetch it's data. I would like to define this when I define the dropdown component in the parent.

For Example

<dropdown [service]="myService"></dropdown>

I could then call this function on the dropdown component passing parameters such as pagination and search parameters.

this.service(x,y,z).subscribe()

Is this possible?

I am also open to any other suggestions especially if I am looking at this the wrong way?

Upvotes: 3

Views: 5931

Answers (2)

lealceldeiro
lealceldeiro

Reputation: 14958

Yes, it is possible. You could do something like this (StackBlitz):

1- Define an @Input() property in the DropDownComponent receiving the service (I created an interface in this case for better design, Logger).

@Component({
  selector: 'dropdown',
  template: `Dropdown using {{this.logger.getName()}}`
})
export class DropDownComponent implements OnInit {

  @Input() public logger: Logger;

  constructor() { }

  ngOnInit() {
    console.log(`DropDown, I'm using `, this.logger.getName());
  }
}

2- In the component that uses the drop down then define the value for the logger property.

@Component({
  selector: 'my-app',
  template: `
  <dropdown [logger]="this.logger1Service" ></dropdown>
  <br />
  <dropdown [logger]="this.logger2Service" ></dropdown>
  `
})
export class AppComponent implements OnInit {

  constructor(
    public logger1Service: Logger1Service,
    public logger2Service: Logger2Service
    ) { }

  ngOnInit() {
    console.log('Hello from AppComponent');
    console.log('calling logger1Service.info() with message X', this.logger1Service.info('X'));
    console.log('calling logger2Service.info() with message Y', this.logger1Service.info('Y'));
  }
}

3- And define the services that could be provided for this dropdown.

// logger.interface
export interface Logger {
  info(text: string);
  getName();
}

// logger1.service
import { Injectable} from '@angular/core';

import { Logger } from './logger.interface';

@Injectable({ providedIn: 'root' })
export class Logger1Service implements Logger {

  constructor() { }

  info(text: string) {
    console.log('Logger 1:', text);
  }

  getName() {
    return 'Logger1Service';
  }
}

// logger2.service
import { Injectable} from '@angular/core';

import { Logger } from './logger.interface';

@Injectable({ providedIn: 'root' })
export class Logger2Service implements Logger {

  constructor() { }

  info(text: string) {
    console.log('Logger 2:', text);
  }

  getName() {
    return 'Logger2Service';
  }
}

Upvotes: 3

Steve
Steve

Reputation: 602

It's better to wrap this dumb component into container and reuse it. You smart component will inject service with specific interface. The service you can declare in the parent component to container.

I created stack blitz demo

Upvotes: 0

Related Questions