I'm pretty much informed of how Angular's Change Detection works, as well as how we can use OnChanges hook for detecting @Input
properties changes, and also a subscribing to ngModel valueChanges in for example a Directive or a Component etc..
Can anyone exaplain what is happening here:
# Custom Directive:
Let's say we have a custom Directive myNumber which has an @Input() property ngModel:
@Directive({
selector: "[myNumber]"
})
class MyNumberDirective implements OnChanges {
@Input() ngModel: any;
constructor(private model: NgModel) {
this.model.control.valueChanges.subscribe(data => {
console.log('directive model changes detected by model control value change subscription');
});
}
ngOnChanges(changes: SimpleChanges){
if(changes.ngModel){
console.log('directive input ngModel changes detected by OnChanges hook');
}
}
}
@Input
property ngModel and directive's model object changes. Changes should be logged in the console when model value changes.# Component's template:
<input type="number" myNumber [(ngModel)]="number1" />
<input type="number" myNumber [(ngModel)]="number2" />
<input type="number" myNumber [(ngModel)]="number3" (blur)="calculate()" />
We applied myNumber directive on three input elements and each input element has ngModel: number1, number2, number3.
Last input has on blur event to call calculate() method.
# Component's typescript:
calculate() {
this.number1 = 10; // changing ngModel of first input
console.log('number1 changed in a calculate method');
this.number2 = 20; // changing ngModel of second input
console.log('number2 changed in a calculate method');
this.number3 = 30; // changing ngModel of third input
console.log('number3 changed in a calculate method');
}
# Problem:
Angular will execute calculate()
method, change all three models and then detect changes and trigger cd hooks in a Directive:
// calculate() log messages first:
'number1 changed in a calculate method'
'number2 changed in a calculate method'
'number3 changed in a calculate method'
// then number1 model change messages in a directive:
'directive model changes detected by model control value change subscription'
'directive input ngModel changes detected by OnChanges hook'
// then number2 model change messages in a directive:
'directive model changes detected by model control value change subscription'
'directive input ngModel changes detected by OnChanges hook'
// then number3 model change messages in a directive:
'directive model changes detected by model control value change subscription'
'directive input ngModel changes detected by OnChanges hook'
# Solution I would like to simplify:
In the component we can call changeDetection()
after each model change in the calculate()
method. That will trigger directive's change detection hook automatically.
constructor(private ref: ChangeDetectorRef) {}
calculate() {
this.number1 = 10; // changing ngModel of first input
console.log('number1 changed in a calculate method');
this.ref.detectChanges(); // triggering detect changes manually
this.number2 = 20; // changing ngModel of second input
console.log('number2 changed in a calculate method');
this.ref.detectChanges(); // triggering detect changes manually
this.number3 = 30; // changing ngModel of third input
console.log('number3 changed in a calculate method');
this.ref.detectChanges(); // triggering detect changes manually
}
# Question:
How to achieve this immediately and change detections without manually writing ref.detectChanges()
after each model change ?
I hope this example will be useful to all you guys having the same problems
Update:
The above example in my first post is actually working :) it was my mistake and I will explain it in detail.
In my real-world implementation I'm applying number directive to all number input fields. A directive is listening on blur model changes and applying some number rounding.
Everything works fine -> we insert a number and onBlur number will be rounded, applied pipe, etc..
The problem was when I had additional calculation method to re-calculate another fields:
For example:
calculate() {
this.number1 = 10; // changing ngModel of number1
// after model change number1 will be rounded (handled) in a directive
const number2 = 20; // this is not a model so it's not handled by directive
// I used non-model variable in my calculation that is not handled by directive
this.number3 = this.number1 * number2; // changing ngModel of number3
}
By using non-model variable in a calculation - that value is not rounded so I had mismatches in method vs directive calculation rounding resulting in a slightly different number.
That's why I thought that Angular (directive) is not detecting changes in a right moment.
Sorry guys for that, but I hope this example of subscribing to detect changes will help someone !!
:: cheers ::
Josip