Angular2 passing function as component input is not working

18,344

Solution 1

You need to .bind(this) if you pass methods around:

<custom-element [valFn]="customVal.bind(this)"></custom-element>

or

export class App {
  constructor(private service: CustomService) {
  }
  customVal(): string {
    return this.service.getVal();
  }
  customValFn = this.customVal.bind(this);
}

with

<custom-element [valFn]="customValFn"></custom-element>

Solution 2

You can pass a get/set property instead of a function in a similar way like that:

Somewhere in your view:

<input type="text" [(ngModel)]="yourprop">

In your component file:

@Component({
  selector: 'myapp',
  templateUrl: './myapp.component.html',
  styleUrls: ['./myapp.component.scss']
})
export class App {
  constructor() { }

  yourprop: any;

  get yourprop(): any {
    return this.scheduleEndDate;
  };

  //set accessor including call the onchange callback
  set yourprop(v: any) {
    // TODO do something else
    // You can do whatever you want just like you have passed a function

    if (v !== this.scheduleEndDate) {
      this.scheduleEndDate = v;
    }
  }

}

more info @ https://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

Share:
18,344
Ashok Koyi
Author by

Ashok Koyi

Updated on June 17, 2022

Comments

  • Ashok Koyi
    Ashok Koyi about 2 years

    I've a component that takes function as input. I've passed this function from parent.

    Though the function is called, the function is not able to access the dependencies of the instance on which this function is declared.

    Here is the component

    @Component({
      selector: 'custom-element',
      template: `
        {{val}}
      `
    })
    export class CustomElement {
      @Input() valFn: () => string;
    
      get val(): string {
        return this.valFn();
      }
    }
    

    Here is how the component is used

    @Injectable()
    export class CustomService {
      getVal(): string {
        return 'Hello world';
      }
    }
    
    @Component({
      selector: 'my-app',
      template: `
       <custom-element [valFn]="customVal"></custom-element>
      `,
    })
    export class App {
      constructor(private service: CustomService) {
      }
      customVal(): string {
        return this.service.getVal();
      }
    }
    

    When I run this app, I get an error in the console saying Cannot read property 'getVal' of undefined

    Here is a plunker for the issue.

    https://plnkr.co/edit/oQ229rXqOU9Zu1wQx18b?p=preview

  • Ashok Koyi
    Ashok Koyi over 7 years
    Thanks :). That works. I thought the context on which this function is run is already set as it is referred on the App instance
  • Ashok Koyi
    Ashok Koyi over 7 years
    Any idea on why this context is lost in case of function, whereas it can refer to correct this incase of properties?
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    This is "normal" ;-) or "default" JS behavior. If you pass methods or functions around that refer to this, you have to either use .bind(this) or arrow functions (like [valFn]="() => customVal()" but that's not supported in template bindings AFAIR.
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    The "default" behavior is that this points to the caller of the function, not the declarer.
  • Ankit
    Ankit over 6 years
    I want to add one more thing, if you inject CustomerService into your custom-element component then it will work fine, in that case you need not to bind this explicitly. @gunter is right BTW, this holds the context of the current class not the class where method is declared.
  • Steve
    Steve about 6 years
    Is this preferred, ie. why not setup an emitter ?
  • Günter Zöchbauer
    Günter Zöchbauer about 6 years
    @Steve event binding is definitly preferred if possible, but if someone asks for passing methods I try to answer that.
  • Yukun
    Yukun almost 6 years
    Hi @GünterZöchbauer, if an emitter is the preferred solution, can you please provide an example? or any reference? thank you
  • Günter Zöchbauer
    Günter Zöchbauer almost 6 years
    @Yukun is there an Angular introductorial that doesn't explain @Output() with EventEmitter?