10

I have an angular component which has an input Person

export class Person {
  public name = 'Initial Name';
}

export class PersonComponent implements OnInit, OnChanges {
  @Input() public person: Person;

  ngOnChanges(changes: SimpleChanges): void {
    console.log('changed');
  }
}

In the parent component of this component, if I replace the Person object which is given as the input to the child component, ngOnChanges() gets fired in the child component.

But, if I only change the name of the Person object from the parent component, ngOnChanges() doesn't get fired in the child component (anyway, if I bind the person name to an html element in the child template, it gets updated).

Is there any way to get notified in the child component when a property of an input is changed?

4 Answers 4

11

No, the ngOnChanges hook will only fire when the reference changes. You can easily achieve what you're going for by doing this when changing the name property in the parent component:

this.person = {...this.person, name: 'new value'};
Sign up to request clarification or add additional context in comments.

5 Comments

My actual object is much complex than this and I only posted it this way for simplicity. It would be great if there is another way to achieve this.
Doesn't really matter how deep is your object. Immutability is the way to go here :)
Unless you want to pass a given property as a separated input which would probably not be a good idea
@LahiruChandima, ...this.person is the spread operator. It'll copy all of the object's properties, and it'll be overriden by anything to the right, in this case the name property. So all of the person's properties will remain the same except for the name
This is only a partial answer. It solves one of the problems of mutating data, but ignores the bigger issue of why you should use immutable data with Angular. It's a very broad topic ranging from freezing objects to DOM rendering performance. If you've already designed your Angular application to use mutating data then you've painted yourself into a corner already but probably don't know it yet. Next will come questions about state management, mixing methods and properties on frozen data, etc.. etc.. Stop now, Google immutable Javascript and learn about OnPush else you'll have more issues.
4

You could always create an Observable on the Person that emits whenever a property changes.

import { Subject } from 'rxjs';

export class Person {

  private _changes = new Subject<any>();
  private _name = 'Initial Name';

  // subscribe to this one
  public changes = this._changes.asObservable();

  set name(newName: string) {
    this._name = newName;
    this._changes.next({'name': newName});
  }

  get name() {
    return this._name;
  }

}

This way, you can subscribe to person.changes, which will emit a value every time name is changed

When you write person.name = 'Fred', the setter will be invoked, which in turn will emit the new value from the Observable.

Comments

-1

If you use a getter, each time you give a value the setter is executed. So a work-around can be have two variables

  @Input() person;
  @Input() 
  set changePerson(value)
  {
    console.log(this.person);
  }

So, in your parent you can write

  <app-child [person]="person" [changePerson]="changePerson"></app-child>

  this.person.name="new name";
  this.changePerson=true;

Another option is using a EventEmitter in a service,each time you change a

    //Our service
    @Injectable({
      providedIn: 'root',
    })
    export class ChangeService {
      changed: EventEmitter<any>=new EventEmitter();
      emit(value:any)
      {
          this.changed.emit(value);
      }
    }

//Our parent
constructor (private changeService:ChangeService){}
  ngOnInit()
  {
    setTimeout(()=>{
      this.changeService.emit(this.person)

    })
    setTimeout(()=>{
      this.person.name="George";
      this.changeService.emit(this.person);
    },5000);
  }

//Our child
  person:any;
  ngOnInit() {
    this.changeService.changed.subscribe(res=>{
      this.person=res;
      console.log(res);
    })
  }

Comments

-2

Why do you need your child to know about the change? in your child you should only change things related to the child and bubble them up to the parent. With Observables it is easier for the child to still display the information. I recommend using @Output and then changing the object in the parent

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.