Weirdest bug ever!? This HTML view behaviour will shock you (Angular change detection)

Hello everyone! :slight_smile:

TLDR: Angular’s change detection somehow doesn’t work on a phone. View updates slowly after random time intervall.

I stumbled upon the weirdest bug ever. It’s only observable when the app is installed on a phone, but not on when serving it using ionic serve. (Don’t mind the dirty code later pls.)

I have a variable called amount: number = 1;
I have two buttons, a plus and a minus one. Here is their html:

<ion-col col-4 text-right (click)="changeAmount(false)">
  <ion-icon name="my-minus" class="popover_icon disabledColor" id="popover_icon_minus"></ion-icon>
</ion-col>
<ion-col col-4 text-center>
  {{amount}}
</ion-col>
<ion-col col-4 (click)="changeAmount(true)">
  <ion-icon name="my-plus" class="popover_icon"></ion-icon>
</ion-col>

And here is their corresponding logic:

changeAmount(increasing: boolean): void{
  setTimeout( () => {
    increasing ? this.amount ++ : (this.amount > 1 ? this.amount -- : this.amount);
    if(this.amount == 1) {
      document.getElementById("popover_icon_minus").classList.add("disabledColor");
    } else {
      document.getElementById("popover_icon_minus").classList.remove("disabledColor");
    }
    this.hackyResize();
  },10)
}
private hackyResize(): void {
  // XXX: What the f*ck happens with the view?!
  window.dispatchEvent(new Event('resize'));
}

As you can see these methods are very unclean. But this is the only way I was able to make it work.

Expected behaviour:
As soon as someone clicks a button the number changes according to what he has clicked.
Observed behaviour (without my dirty functions):
When someone clicks and changes the amount it takes a random time until the view updates and the number on screen changes. (The disabled class gets applied on the button instantly, therefore I know that the number gets changed instantly when the number is reduced from two to one or increased from one to two. The bug can’t / shouldn’t be in the code’s logic.)

I solved this problem forcing a resize of the screen every time the function is called. But this shouldn’t happen. What’s wrong with Angular’s change detection? Or did I do something wrong?

Thank you for any help!

Your prose in this post contains type errors. You talk about buttons, but there are no buttons in your html.

Sorry if I might have been unclear, englisch is not my first language.

I thought it was clear, that the buttons I was referring to, where the columns which had a click method on it :slight_smile:
This one: <ion-col col-4 text-right (click)="changeAmount(false)">,
And this one: <ion-col col-4 (click)="changeAmount(true)">.

Those aren’t buttons. You would not have this problem if you were using buttons. They are clickable Ionic components. Ionic is built on top of Angular, so its components may or may not be inside the Angular ViewContainer, depending on what you are doing. ion-select and ion-checkbox are not checked by Angular change detection, for example.

You can add the tappable directive, to see if that helps.
<ion-col tappable></ion-col>
In general, though, if you want the user to interact with a pure Ionic component, you might have to fire change detection manually.

1 Like

Thank you so much, that clarifies a lot!

I do have a similar problem on the same page, that my checkboxes do not get updated.

How do I fire change detection manually for buttons as well as for checkboxes?

By injecting ChangeDetectorRef.

1 Like

Thanks I will try that out.

You can definitely make divs clickable, they absolutely do not have to be buttons. I can’t reproduce your bug, this works totally fine for me both in chrome and running on my android.

Try here: https://plnkr.co/edit/rHIksW?p=preview

Notice I don’t have your classes you have so I just put a + and a - inside each column.

If you want to test on mobile, open this on your phone: https://run.plnkr.co/preview/cjdkcoj2d000bfill8bkbfltw/

It all works perfectly fine for me. Does it not to you? If tell us what phone model you’re using.

1 Like

That’s what I do actually.
<div tappable (click)><ion-card></ion-card></div>
That way the click is on something I know Angular will recognize across platform. I almost never listen for a click inside an Ionic component, just in span or div.

Thank you for your answer! I tested on several Android devices actually. The only one where it was instant without my hacky rework was a google pixel 2.

Otherwise on like a Samsung Galaxy S7 for example, or and iPhone 8 it would show the described observed behaviour.

It might have something to do with the stuff wrapped around it.

<ion-footer>
  <ion-grid class="selectableButtons">
  <ion-row>
      <ion-col col-4 text-right (click)="changeAmount(false)">
        <ion-icon name="my-minus" class="popover_icon disabledColor" id="popover_icon_minus"></ion-icon>
      </ion-col>
      <ion-col col-4 text-center>
        {{amount}}
      </ion-col>
      <ion-col col-4 (click)="changeAmount(true)">
        <ion-icon name="my-plus" class="popover_icon"></ion-icon>
      </ion-col>
    </ion-row>
  </ion-grid>
  <ion-grid>
      <ion-row>
          <ion-col col-12>
              <ion-item>
                  <ion-label floating>someWord</ion-label>
                  <ion-input [(ngModel)]="someModel"></ion-input>
                </ion-item>
          </ion-col>
      </ion-row>
    </ion-grid>
    <ion-grid>
      <ion-row>
        <ion-col>
          <button ion-button full (click)="someFunc()">someWord</button>
        </ion-col>
        <ion-col>
          <button ion-button full (click)="someFunc()">someWord</button>
        </ion-col>
      </ion-row>
    </ion-grid>
</ion-footer>

So there is a lot of ionic generated stuff going on. Might disturb the view…

Does the code I posted work on your pixel 2? Coincidentally that’s the phone I have and where my code ran just fine.

I’m much more concerned with your code that modifies the class of the grid cells than any of the html, since you’re not really doing that in in the angular way.

But regarding the code you posted there, there is no reason to use multiple grids. Use one grid, put multiple rows in it. Otherwise I don’t really see any issues. The big thing is…does my code work where yours doesn’t?

Except button and a, tappable is a must, it can’t works correctly on real devices without it

I use tappable and click on elements like div, ion-card. ion-item, p, it does the job

Okay thanks for the tip. I will try your code out and get back to you!

see here https://www.joshmorony.com/understanding-zones-and-change-detection-in-ionic-2-angular-2/
and my post of the same kind of problem
Item click event results not propogated, resolved

1 Like

on another note, please don’t use click-bait titles

Okay, so what did it for me was using (tap)=“myFunc()”. Tappable didn’t do anything.

But thank you all for your help!

Why? They work and help posts stand out :slight_smile: I would say this one went pretty well!