Jan Elznic
Jan Elznic

Reputation: 195

Function ngOnChanges() is not working in Angular 5

I'm trying to call function ngOnChanges() in my Angular 5.x component whenever the variable this.test in component lifecycle or in template is changed but it's not working, ngOnChanges() function is not called anytime. Please can someone help me?

src/app.ts:

import {Component, NgModule, Input, OnChanges, SimpleChanges} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="text" placeholder="Test field" value="{{ test }}">
    </div>
  `,
})
export class App implements OnChanges {
  @Input() test: string;
  name: string;
  constructor() {
  }

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

    if (changes.test && !changes.test.isFirstChange()) {
      // exteranl API call or more preprocessing...
    }

    for (let propName in changes) {
      let change = changes[propName];
      console.dir(change);
      if(change.isFirstChange()) {
        console.log(`first change: ${propName}`);
      } else {
        console.log(`prev: ${change.previousValue}, cur: ${change.currentValue}`);
      }
    }
  }


}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

Live preview: https://plnkr.co/edit/ZHFOXFhEkSv2f1U3lehv

Thanks a lot!

Upvotes: 4

Views: 17582

Answers (2)

DeborahK
DeborahK

Reputation: 60518

Input properties provide a mechanism for a parent component to pass data to a child component. They are NOT meant to pass data from a template to its component.

And only changes to that input property defined by the PARENT generate the onChanges method.

I updated the plunker to fix the missing FormsModule and add a child component to demonstrate how to use the input property and onChanges lifecycle hook:

https://plnkr.co/edit/1JF0wV28HnjXDZxMSifY?p=preview

Child Component

@Component({
  selector: 'my-child',
  template: `
    <div>
      <input type="text" [(ngModel)]="test" placeholder="Test field">
    </div>
  `,
})
export class ChildComponent implements OnChanges {
  @Input() test: string;
  name: string;
  constructor() {  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('in ngOnChanges');
    if (changes.test && !changes.test.isFirstChange()) {
      // exteranl API call or more preprocessing...
    }

    for (let propName in changes) {
      let change = changes[propName];
      console.dir(change);
      if(change.isFirstChange()) {
        console.log(`first change: ${propName}`);
      } else {
        console.log(`prev: ${change.previousValue}, cur: ${change.currentValue}`);
      }
    }
  }
}

Parent component

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <my-child [test]='parentTest'></my-child>
      <button (click)='onClick()'>Change Value</button>
    </div>
  `,
})
export class App {
  parentTest: string;
  name: string;
  counter = 1;

  constructor() {
    this.name = `Angular! v${VERSION.full}`
  }

  onClick() {
    this.parentTest = `test: ${this.counter}`;
    this.counter++;
  }
}

To catch changes from the template in the template's component, use a setter instead:

  // To catch changes from the template
  _test: string;
  get test(): string {
    return this._test;
  }

  @Input()
  set test(value: string) {
    this._test = value;
    console.log("Textbox value changed: " + this._test);
  }

Or you can use Sajeetharan's suggestion to catch the template's change in its associated template. It will also work.

Upvotes: 7

Sajeetharan
Sajeetharan

Reputation: 222582

What you need here is ngModelChange not ngOnChanges

<input type="text" placeholder="Test field" (ngModelChange)="printVal()">

and then

printVal(){
  //detect model change here
}

ngOnChanges works over @input event emitter

Upvotes: 5

Related Questions