PaulBunion
PaulBunion

Reputation: 405

Angular input signal with function type

I have an old @Input that passes a callback function from the parent component, which I'd like to convert to an input signal.

Using the old decorator input, I can call the passed-in function simply by doing this.callback(). This of course doesn't work with an input signal because the same statement simply returns the input's value...which in this case is a function, so I would think this.callback()() would work to get the value of the input (the function) and then actually call the function, but it seems it does not.

If I pass a function to an input signal (currently defined as callback = input.required<() => void>()), is there a way to actually call the function, or do I just need to stick with the old decorator input for now?

Parent:

@Component({})
class ParentComponent {
  parentFunc() {}
}
<app-child-component [callback]="parentFunc" />

Child:

@Component({})
class ChildComponent {
  callback = input.required<() => void>();

  ngOnInit() {
    // doesn't work even though I'd expect it to
    this.callback()();
  }
}

Upvotes: 1

Views: 63

Answers (2)

Matthieu Riegler
Matthieu Riegler

Reputation: 55669

Make your callback an arrow function instead of a regular method. This way you'll keep the this context.

@Component({
  selector: 'app-root',
  imports: [Child],
  template: `
    <app-child [callback]="callback">
  `,
})
export class App {

  callback = () => {        
    console.log('from parent', this.name);
  }
}

Upvotes: 1

Naren Murali
Naren Murali

Reputation: 57986

Make sure you have .bind(this) to the callback so that it executes on the parent scope.

The callback also should be passed as is, without executing it.

import { Component, input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-child',
  template: `
    <button (click)="clickEvent()">click</button>
  `,
})
export class Child {
  callback = input.required<() => void>();

  clickEvent() {
    this.callback()();
  }
}

@Component({
  selector: 'app-root',
  imports: [Child],
  template: `
    <app-child [callback]="callback.bind(this)">
  `,
})
export class App {
  name = 'Angular';

  callback() {
    console.log('from parent', this.name);
  }
}

bootstrapApplication(App);

Stackblitz Demo

Upvotes: 1

Related Questions