2

I have a component as follows:

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'app',
    moduleId: module.id,
    template: `
        <input [value]="a" (change)="onValidateOfA($event)" />
`,

})
export class AppComponent implements OnInit {

    a: number = 10;

    constructor() { }

    ngOnInit() {
    }

    onValidateOfA(e: any) {
        let t = e.target.value * 1;
        if (t < 10) {
            t = this.a;
        }
        this.a = t;
    }
}

The logic is simple. I would like to switch to previous value in the textbox when the user types any value less than 10. The value gets changed in variable "a", but not getting reflected in textbox (using property binding). The value in textbox does not always represent the same value in 'a' (or how to force as such in this case).

I am kind of learning detection changes in Angular and would like to resolve the problem using that rather than trying to solve the problem in a very different approach.

I tried to follow everything mentioned at following:

But with no luck or I am missing something (no error, just simply the same problem).

shaunhusain
  • 19,630
  • 4
  • 38
  • 51
user203687
  • 6,875
  • 12
  • 53
  • 85

2 Answers2

0

If you have code that executes outside of the Angular context e.g setTimeout() {} you can do this:

public ngZone: NgZone 

Then any code you want to be applied immediately in angular:

this.ngZone.run(() => { 
  //Code here...
});

To be honest though, you shouldn't need it in this case.

williamsandonz
  • 15,864
  • 23
  • 100
  • 186
  • Just for my knowledge. I tried using "this.ngZone.run( () => {this.a = t})" replacing "this.a = t" in above code. Still it didn't work. While this may not be necessary, I am just curious on how we can make it work using ngZone. When you get a chance, do you mind modifying the above sample to achieve the same using ngZone. Thank you. – user203687 Jan 15 '18 at 02:27
0

The issue here isn't change detection. You can verify this by adding an interpolation next to the <input> to echo the value of this.a:

Value: {{ a }}<br />
<input [value]="a" (change)="onValidateOfA($event)" />

When a passes validation and the field is defocused, the value clearly updates. So why isn't the field updating?

This is because of how HTML forms are implemented. Angular uses a FormsModule to interface with forms like this, however a quick fix is to just set the value directly:

onValidateOfA(e: any) {
    let t = e.target.value * 1;
    if (t < 10) {
        t = this.a;
    }
    this.a = t;
    e.target.value = t;
}

A better way would be to use the FormsModule and add a layer of indirection. See the plunker here:

https://plnkr.co/edit/b8D3rMpFaw1oTmrGirDW?p=preview

What we do here is keep track of a rawFoo and a validatedFoo. The validatedFoo is always valid, but the rawFoo is two-way bound to the form using ngModel.

Here's some further reading on ngModel from the Angular docs:

https://angular.io/guide/forms


As an aside, the 'right' way to do this is with custom field validators, explained in the Angular docs here:

https://angular.io/guide/form-validation#custom-validators

The basic premise is that you write a function that returns null for valid inputs, and a descriptive object otherwise. You then add some view logic that lets the user know the value is wrong, while the framework takes care of keeping everything in a valid state.

Joe Quigley
  • 175
  • 1
  • 8