Home > Software design >  Invoke function in child component using a service injected in parent
Invoke function in child component using a service injected in parent

Time:06-09

I have a function using a service call that must be invoked in both parent and child component.

@Component({
  selector: 'my-app',
  template: `
    <app-child [callTheObservable]="callTheObservable"></app-child>
    <button (click)="callTheObservable()">button</button>
  `,
  styleUrls: ['./parent.component.css'],
})
export class ParentComponent {
  constructor(private customService: CustomService) {}

  callTheObservable() {
    this.customService.anObservable.subscribe((res) => {
      console.log(res);
    });
  }
}

Child component gets the function through Input but when function is called, the service is not recognized.

export class ChildComponent implements OnInit {
  @Input() callTheObservable: () => void;

  constructor() {}

  ngOnInit() {
    this.callTheObservable();
  }
}

Error: Cannot read properties of undefined (reading 'anObservable')

I had to inject the service to get recognized but now I have a service that is never used.

constructor(private customService: CustomService) {}

An easy way to achieve it is by emitting an event but I was wondering if I could make it by Input but without injecting the service in child component?

The problem is solved when injecting the service in child component because of singleton service, so both in parent and child we inject the same reference?

Here is Stackblitz code

CodePudding user response:

You send the reference but it does not have access to this from the parent.. you can solve it like this:

@Component({
  selector: 'my-app',
  template: `
    <app-child [callTheObservable]="reference"></app-child>
    <button (click)="callTheObservable()">button</button>
  `,
  styleUrls: ['./parent.component.css'],
})
export class ParentComponent {
  reference = this.callTheObservable.bind(this);

  constructor(private customService: CustomService) {}

  callTheObservable() {
    this.customService.anObservable.subscribe((res) => {
      console.log(res);
    });
  }
}

CodePudding user response:

You're passing a function as an input. This function has a reference to this in its body: this.customService. And when inside the child component this refers to that same child component. And child component indeed doesn't have a property customService.

So, you need to define it somehow.

1 way - inject the service inside the child component as well and do not the function via input.

2 way - pass the function with a context binding inside the parent component like this: <app-child [callTheObservable]="callTheObservable.bind(this)"></app-child>

CodePudding user response:

Normal functions do not maintain their original lexical context, so referring to this inside the function refers to whatever object the function is executing inside.

Arrow functions do maintain their original lexical context, so referring to this inside the function will refer to the original this when the function was defined.

So just make it an arrow function.

export class ParentComponent {
  constructor(private customService: CustomService) {}

  callTheObservable = () => {
    this.customService.anObservable.subscribe((res) => {
      console.log(res);
    });
  }
}
  • Related