215
votes

I have an Angular2 component in that component it currently has a bunch fields that have @Input() applied before them to allow binding to that property, i.e.

@Input() allowDay: boolean;

What I would like to do is actually bind to a property with get/set, so that I can do some other logic in the setter, something like the following

_allowDay: boolean;
get allowDay(): boolean {
    return this._allowDay;
}
set allowDay(value: boolean) {
     this._allowDay = value;
     this.updatePeriodTypes();
}

how would I do this in Angular2?

Based on Thierry Templier suggestion I changed it to, but that throws the error Can't bind to 'allowDay' since it isn't a known native property :

//@Input() allowDay: boolean;
_allowDay: boolean;
get allowDay(): boolean {
    return this._allowDay;
}
@Input('allowDay') set allowDay(value: boolean) {
    this._allowDay = value;
    this.updatePeriodTypes();
}
4
How and where to do you bind to [allowDay]="....". If the field (setter) name and the property name you want to use for binding are the same, you can omit the parameter for @Input(...)`.Günter Zöchbauer
I would be curious to see how yo set up your unit test if you went the route of using get set as shown in the accepted answer.Winnemucca
Whatever you end up doing make sure to put a breakpoint, or debug statement, or counter inside your setter to make sure it is only firing once as expected. I just found mine was being updated for every change detection run causing horrible performance and quirky behavior.Simon_Weaver

4 Answers

351
votes

You could set the @Input on the setter directly, as described below:

_allowDay: boolean;
get allowDay(): boolean {
    return this._allowDay;
}
@Input() set allowDay(value: boolean) {
    this._allowDay = value;
    this.updatePeriodTypes();
}

See this Plunkr: https://plnkr.co/edit/6miSutgTe9sfEMCb8N4p?p=preview.

71
votes

If you are mainly interested in implementing logic to the setter only:

import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

// [...]

export class MyClass implements OnChanges {
  @Input() allowDay: boolean;

  ngOnChanges(changes: SimpleChanges): void {
    if(changes['allowDay']) {
      this.updatePeriodTypes();
    }
  }
}

The import of SimpleChanges is not needed if it doesn't matter which input property was changed or if you have only one input property.

Angular Doc: OnChanges

otherwise:

private _allowDay: boolean;

@Input() set allowDay(value: boolean) {
  this._allowDay = value;
  this.updatePeriodTypes();
}
get allowDay(): boolean {
  // other logic
  return this._allowDay;
}
9
votes

@Paul Cavacas, I had the same issue and I solved by setting the Input() decorator above the getter.

  @Input('allowDays')
  get in(): any {
    return this._allowDays;
  }

  //@Input('allowDays')
  // not working
  set in(val) {
    console.log('allowDays = '+val);
    this._allowDays = val;
  }

See this plunker: https://plnkr.co/edit/6miSutgTe9sfEMCb8N4p?p=preview

1
votes

Updated accepted answer to angular 7.0.1 on stackblitz here: https://stackblitz.com/edit/angular-inputsetter?embed=1&file=src/app/app.component.ts

directives are no more in Component decorator options. So I have provided sub directive to app module.

thank you @thierry-templier!