Reputation: 195
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
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
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