15

I've got an Angular 2 Controller which looks like this:

@Component({
  selector: 'my-component',
  template: '<div>The value is: {{ value }}</div>',
})
class MyComponent implements OnInit {
  @Input()
  value: string;

  @Output
  valueChange = new EventEmitter<number>();

  ngOnInit() {
    this.valueChange.subscribe(value => {
      console.log('new value:', value); // <-- does not get triggered
    });
  }
}

But when the value of value changes from a template binding:

<my-component [value]="someValue" /> <!-- valueChange not triggered! -->

The valueChange event isn't triggered so, even though the template correctly updates and shows the new value, the component doesn't know it's been changed.

How can I detect when input attributes on my controller are changed?

0

3 Answers 3

20

In my opinion, Output is for your component to emit event to others so they would update their view if necessary, it should only be used for changes made internally that need to be broadcast out (hence the name Output). Triggering an Output event on Input changes may cause unexpected behavior (as now that valueChange is triggered for all the changes both inside and outside, and not really an Output anymore).

In your case, if you want to do stuff when your value changes, you can make it a setter:

private _value: string;
get value(): string {
  return this._value;
}

@Input('value')
set value(val: string) {
  this._value = val;
  console.log('new value:', value); // <-- do your logic here!
}
Sign up to request clarification or add additional context in comments.

1 Comment

Looks elegant, and even works!
8

I've used setters for detecting when input variables have been modified. You have to use the inputs key in the @Component decorator instead of the @Input declaration to get this to work, like so:

@Component({
  selector: 'my-component',
  template: '<div>The value is: {{ value }}</div>',
  inputs: ['value']
})
class MyComponent {
  private _value: string;

  @Output
  valueChange = new EventEmitter<number>();

  set value(value) {
    this._value = value;
    console.log('new value:', value);
    this.valueChange.emit(value);
  }

  get value(value) {
    return this._value;
  }
}

Also FYI, the class method is ngOnInit instead of onInit.

2 Comments

You can use the inputs property on the \@Component decorator or you can use the \@Input decorator on the set property. They both currently work, and the \@Input decorator seems to be the current preferred way
A get accessor should and cannot have a value, you may want to update your get method to get value() {} I think your @output may also need to be updated to @Output()
4

I've been able to work around the issue by implementing from OnChanges:

ngOnChanges(changes) {
  for (let key in changes) {
    if (this[key + 'Change'])
      this[key + 'Change'].emit(changes[key].currentValue);
  }
}

Which will automatically trigger the Change event whenever Angular detects a change… but is there any way to do this natively?

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.