0

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?

Presto
  • 888
  • 12
  • 30
  • 3
    Is it because you are setting the input value in `onChange` which technically changes the input value again and call the method. This continues infinite. – Maihan Nijat Dec 12 '19 at 13:54
  • @MaihanNijat it's has a sense but how can I parse the right type of value to input after changed it? Because I've got a string there instead of number. – Presto Dec 12 '19 at 13:56
  • I know you found the aswer on your question but I think the problem is the usage of the `[formControlName]="formName"` and `(ngModelChange)="onChange($event)"` at time on one input – Ashot Aleqsanyan Dec 12 '19 at 14:43

2 Answers2

2

It could be because of setting the input value in onChange function which changes the input value again and calls onChange again. This continues infinitely.

Cast it to a number when you need it, instead of parsing a string as number and then set it to the control back which is unnecessary.

parseFloat(this.form.get(this.formName).value)

Or

+this.form.get(this.formName).value

In this case, you don't need to parse it as number each time you change the value, but you parse it when you need it.

You can use one of the above lines to parse an input to a number with each change but don't set it to the same control again.

onChange(newValue: string) {
    parseFloat(this.form.get(this.formName).value)
}
Maihan Nijat
  • 9,054
  • 11
  • 62
  • 110
1

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.

Matt U
  • 4,970
  • 9
  • 28