301

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();
}
3
  • 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(...)`. Commented Apr 16, 2016 at 10:29
  • 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. Commented Jan 8, 2018 at 22:12
  • 1
    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. Commented Feb 14, 2018 at 23:53

5 Answers 5

496

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.

Sign up to request clarification or add additional context in comments.

10 Comments

I get the following error Can't bind to 'allowDay' since it isn't a known native property. See updated question for exactly what I changed the code to
Are you sure? It works for me. I added a plunkr. Perhaps you forgot to add the directive into the directives attribute of the component where you want to use it... I updated my answer.
This is a bad idea because if you use the setter, ngOnChanges doesn't fire.
WARNING: The setter will NOT be triggered by mutations to values which are passed by reference (i.e. pushing to an array, mutating an object, etc.). You would need to replace the whole value being passed as an Input for the setter to trigger again.
|
102

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 Docs: OnChanges

otherwise:

private _allowDay: boolean;

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

8 Comments

Just curious, is there any benefit for using ngOnChanges vs not using the set property if you are only interested on a setter logic?
There is no difference in "using ngOnChanges vs not using set"... ;) Joking aside: One benefit is, if your component has multiple @Input properties and you want to call a routine when any of them has changed. So less code needed.
Ups, had a typo hehe. But ok, thought it might had more relevance. Thanks for the answer tho :)
@MA-Maddin I suppose you could also set a debounced observable if you were expecting multiple changes all at the same time that would each result in a routine needing to be run.
Heads up... if your input type is an Interface, OnChanges does not catch attribute value changes.
|
14

@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

2 Comments

This bug got me crazy, I finally found that you should define the Input() first (getter or setter but the input decorator must go first)
Here's other reference that may help https://github.com/angular/angular/issues/5477
4

In angular v16.1.0-next.3🎉

We can use transform property on inputs. If an input has a transform function, any values set through the template will be passed through the function before being assigned to the directive instance.

The example from above can be rewritten to the following:

@Input({ transform: booleanAttribute }) allowDay = false;

Example

3 Comments

Can we also add custom logic to format the input?
@Anurag yes you can
Filtering the data here in any way doesn't work. I'm receiving the dataSource for a dropdown here. And if I filter it in any way, the items are no longer clickable in the dropdown. The only way that work is creating a second data-member to store the filtered data. @Input set dataInput(value) { this.data = value; }
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.