Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 5 realtime Change Detection in Directive

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');
    }
  }
}
  • In the above example I set subscription to @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');
}
  • I logged a message after each model change in a calculate() method.
  • A Directive is listening to ngModel changes and it will log a two messages for each model value change as well.

# 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
}
  • This way angular will change model and call change detection hook inside a Directive immediately.

# 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

like image 375
NavCore Avatar asked Apr 09 '18 06:04

NavCore


People also ask

How does Angular detect change detection?

To run the change detector manually: Inject ChangeDetectorRef service in the component. Use markForCheck in the subscription method to instruct Angular to check the component the next time change detectors run. On the ngOnDestroy() life cycle hook, unsubscribe from the observable.

Where should we call the detectChanges () method?

detectChanges — When you call this method on changeDetectorRef provider, it will run change detection from the current component and all it's descendants. While running change detection it keeps the change detection strategy in mind.

What does ChangeDetectorRef detectChanges () do?

detectChanges()linkChecks this view and its children. Use in combination with detach to implement local change detection checks.

What triggers change detection in Angular?

By default, angular will run the change detector every time @Input() data is changed or modified. But with OnPush strategy, the change detector is only triggered if the data passed on @Input() has a new reference.


1 Answers

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

like image 109
NavCore Avatar answered Sep 25 '22 13:09

NavCore