Ion-range responds extremely slowly when bound and inserted via *ngFor


#1

I have the following prop in my data provider/service…

 get Comparisons(): number[] {
    if (this.data == null)
      return [];
    // console.log('PWCInputProvider------------>>Comparisons', r);
    return this.data.HDM.Level[1].E[2].w;
  }

Here is the html

<p>
    Comparisons....
  </p>
  <ion-list>
    <ion-item *ngFor="let w of PWCInputProvider.Comparisons; let i = index">
      <ion-range min="0" max="100" pin="true" debounce="50"  [(ngModel)]="PWCInputProvider.Comparisons[i]"  >
        <ion-label range-left class="small-text">Apples</ion-label>
        <ion-label range-right>Oranges</ion-label>
      </ion-range>
    </ion-item>
  </ion-list>

If I enable the console.log I will see a zillion calls to the Comparisons property while I try to move the slider (range) and it responds very very slowly and the pin won’t even display (not usable).

What am I doing wrong?
I would like the 2-way binding between the ranges and some corresponding model in my data provider class (an array of integers representing the latest values).

Comparisons_: number[] = [];

<ion-item *ngFor="let w of PWCInputProvider.Comparisons_; let i = index">
      <ion-range min="0" max="100" pin="true" [(ngModel)]="PWCInputProvider.Comparisons_[i]"  >

I added debounce attribute and tested various values, 50, 500, 1000 not change in super sluggish response.
I also tried in vain a simple field member in my provider as in:

If I remove the ngModel altogether I get a normal UI response, smooth sliding, pin pops, shows value and has an animation, but of course all ranges inserted in this manner are just vanilla, unbound and no initial value.

UPDATE
It appears as if inserting ranges via the ngFor is the problem. My current test data returns an array of 3 numbers, if I insert an instance of the range bound to the second element as below explicitly before the ngFor, then I get the following behavior. This first instance responds just fine to human interaction and also causes the 3rd instance to sync. However, all 3 instances of the range control inserted via the *nfFor do not respond. Should I report this as a bug?

<ion-range min="0" max="100" pin="true" [(ngModel)]="PWCInputProvider.Comparisons[1]">
    <ion-label range-left class="small-text">Apples</ion-label>
    <ion-label range-right>Oranges</ion-label>
  </ion-range>

  <ion-list>
    <ion-item *ngFor="let w of PWCInputProvider.Comparisons; let i = index">
      <ion-range min="0" max="100" pin="true" [(ngModel)]="PWCInputProvider.Comparisons[i]">
        <ion-label range-left class="small-text">Apples</ion-label>
        <ion-label range-right>Oranges</ion-label>
      </ion-range>
    </ion-item>
  </ion-list>

Thank you.


#2

I would use FormBuilder, define all the ranges in the controller, stored in some Iterable, and then in the template *ngFor over the Iterable you already computed, and use the ionChange events of the ranges to update what’s going on. Ditch ngModel completely. ngModel is great for simple forms, like a radio button. Once you get complicated, there’s a lot of overhead in 2-way binding. Personally, I used to think 2-way binding was super cool when I had only read about it. Now that I have experience programming in Angular and Ionic, my global environment is set to ChangeDetectionStrategy OnPush, and when I want 2-way binding, I build it into the template manually.


#3

@AaronSterling, I have never used FormBuilder. I assume it belongs to Ionic not Angular. I browsed https://ionicframework.com/docs/resources/forms/ but please provide some links so I am not looking at the wrong thing. You suggested “define all the ranges in the controller” I need to build the number of ranges at runtime based on what user inputs or the json restored from a previous inputs dictates. Will the “FormBuilder” allow for a dynamic number of ranges in the view? I am interested in looking at your approach “…ChangeDetectionStrategy OnPush” if you can provide some code or a reference of the same, I would appreciate it very much. I am terrible at grasping new concepts but I do well with cut/paste and pattern matching :slight_smile: Thank you.


#4

See this for more on FormBuilder. I am less enthusiastic than @AaronSterling is about OnPush change detection, and I recommend that if you are unfamiliar with all of the nuances and edge cases involving it, that you just leave it alone and let the default change detection algorithm do its thing.


#5

@rapropos
Here is the plunker of the issue rendering only 3 sliders via ngFor/ngModel, the natural way, the ionic way.

I am still hacking at the link your provided, Angular Reactive Forms, with FormBuilder. I hate it when API “documenters” go on a limb and give a zillion examples in increasing complexity, and worse, they start using these really weird names heroForm, fb, secretLairs, bla bla, just call the darn thing FormGroup or HeroFormGroup, FormBuilder. Sorry fb stands for facebook, can’t use that :slight_smile:

Anyone who looks at the plunker, should question why should it be so difficult to lay few sliders dynamically. “Form” building is the last thing on my mind when I consider laying such a view.
The other night, I think I stopped not knowing how to make the ion-badges showing values of the slider sync.
I was not sure if I had to tag them as well with a “formControlName” as they are simple derived values of the slider current position.
Perhaps tonight I return to it and hack some more…but it feels weird and overly complex to read and implement.