Search code examples
angulartypescriptangular7infinite-loopangular-ngmodelchange

ngModelChange infinite loop on input


I've got a html like this:

<ng-template [ngSwitchCase]="'textbox'">
    <input *ngIf="setting.type==='number'"
           [step]="setting.step"
           [formControlName]="formName"
           [id]="formName"
           [type]="setting.type"
           [placeholder]="setting.placeholder"
           [title]="setting.description"
           (ngModelChange)="onChange($event)">
</ng-template>

and on controller I've got the function onChange:

onChange(newValue: string) {
    if (newValue === undefined)
        return;

    this.form.get(this.formName).setValue(<any>parseFloat(newValue));
}

When I was debugging this call of onChange func then I noticed that is still calling and really don't know why. There I've got a intinite loop.

My angular packages:

"@angular/animations": "8.2.7",
"@angular/cli": "8.3.5",
"@angular/common": "8.2.7",
"@angular/compiler": "8.2.7",
"@angular/core": "8.2.7",
"@angular/forms": "8.2.7",
"@angular/platform-browser": "8.2.7",
"@angular/platform-browser-dynamic": "8.2.7",
"@angular/router": "8.2.7",
"@babel/polyfill": "7.6.0",

Have you got a ideas what might be wrong with my code?


Solution

  • ControlValueAccessor Approach

    See this answer to a similar question. I know that question is not exactly the same, but it appears to be a solid approach as far as I can tell. Instead of using the ngModelChange event, that answer suggests wrapping the input component and implementing a custom ControlValueAccessor - see the documentation.

    Here is a StackBlitz example of this approach. However, it seems to behave just like the blur event, so the ControlValueAccessor in this case would likely be overkill compared to the approach below.

    Blur Event Approach

    Another option would be to use the blur event on your input. Instead of trying to update the value every time it changes, you will just update the value (parse to a float) when the user leaves the control. Something like:

    HTML

    <ng-template [ngSwitchCase]="'textbox'">
        <input *ngIf="setting.type==='number'"
               [step]="setting.step"
               [formControlName]="formName"
               [id]="formName"
               [type]="setting.type"
               [placeholder]="setting.placeholder"
               [title]="setting.description"
               (blur)="onBlur()">   <!-- this line -->
    </ng-template>
    

    Component TypeScript

    onBlur() {
        const value = this.form.get(this.formName).value;
        this.form.get(this.formName).setValue(<any>parseFloat(value));
    }
    

    Example I created on StackBlitz for the blur method.