Skip to content
This repository was archived by the owner on May 4, 2022. It is now read-only.
This repository was archived by the owner on May 4, 2022. It is now read-only.

ion-toggle unnecessarily triggers change event when default value is true #127

Open

Description

@ionitron-bot

Original issue by @cubicleWar on 2016-08-20T22:45:06Z

This is reposting of issue #6144 from beta 4 it appears to have made its way back into beta 11

Short description of the problem:

ion-toggle unnecessarily triggers change event when the default value is true

What behavior are you expecting?

The change event should not fire until the user presses the toggle

Steps to reproduce:

  1. Create an ion-toggle with ngModel bound to a variable with a default value of true
  2. Observe change event firing without any user input

toggle: boolean = true;

<ion-toggle [(ngModel)]="toggle" (ionChange)="onUpdateToggle()">

Other information: (e.g. stacktraces, related issues, suggestions how to fix, stackoverflow links, forum links, etc)

  • This appears to be the same issue as #6144
  • This only fires when the default value is true.

Which Ionic Version? 1.x or 2.x

2.0.0-beta.11

Plunker that shows an example of your issue

Note this is the plunk of the original issue there seems to be no plunk template for beta 11 and this is not an issue in beta 10. I have attached a sample project to demonstrate the problem in beta 11

http://plnkr.co/edit/f2NSVLP1DETbkyFmpSN8?p=preview

Run ionic info from terminal/cmd prompt: (paste output below)

Cordova CLI: 6.3.1
Gulp version: CLI version 3.9.1
Gulp local: Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.11
Ionic CLI Version: 2.0.0
Ionic App Lib Version: 2.0.0
ios-deploy version: 1.8.6
ios-sim version: 5.0.8
OS: Mac OS X El Capitan
Node Version: v4.4.5
Xcode version: Xcode 7.3 Build version 7D175

Activity

  1. javiermrz commented on Aug 18, 2019

    @javiermrz

    Still happening in Ionic 4. Only happens when the default value related with [(ngModel)] is true.
    I looked into the ionic checkbox web component at master and it looks like it has got something to do with the internal value of the checkbox / toggle being 'false', so it detects it as a change, and I don't know why this happens because if you set that Prop to true, it shouldn't initialize it to false.

    However, the first time I go into the page where the element is, everything goes fine, but if I navigate to other screen and go back then the error occurs.

    I ended up here coming from the original thread in bountysource.com. I'm surprised that this has not been fixed after 3 years and 2 mayor versions.. I'll be happy to answer any questions to help solve this.

    This is part of my code:
    component.ts

    showingOptions: { name: string; color: string; isActive: boolean }[] = [
        { name: 'outgoingRecord', color: 'success', isActive: true },
        { name: 'incomingRecord', color: 'medium', isActive: true },
        { name: 'outgoingPrediction', color: 'success', isActive: true },
        { name: 'incomingPrediction', color: 'medium', isActive: true }
      ]
    

    component.html

     <ion-item *ngFor="let data of showingOptions">
        <ion-label>{{ data.name | translate }} </ion-label>
        <ion-checkbox
        (ionChange)="changeComparedData(data.name)"
        color="{{ data.color }}"
        slot="start"
        [(ngModel)]="data.isActive"
         ></ion-checkbox>
     </ion-item>
    

    If I set isActive values in showingOptions to false, the error won't occur.
    I also tried to set the checkbox checked property to true by default, but it didn't work.

  2. javiermrz commented on Aug 19, 2019

    @javiermrz

    So, I managed to fix the problem with a workaround that is quite simple, so I wouldn't even call it a workaround. Instead of using (ionChange), I used (click) so it won't get triggered by internal changes in the web component, but only by user interactions.

    Final html:

     <ion-item (click)="changeComparedData(data.name)" *ngFor="let data of showingOptions">
        <ion-label>{{ data.name | translate }} </ion-label>
        <ion-checkbox
        color="{{ data.color }}"
        slot="start"
        [(ngModel)]="data.isActive"
         ></ion-checkbox>
     </ion-item>
    
  3. davidseek commented on Feb 23, 2020

    @davidseek

    Still happening in Ionic 5. It has been open since like 3 years without an official solution...

  4. aaronwJordan commented on Apr 25, 2020

    @aaronwJordan

    Would like to see this bug fixed in Ionic v5!
    (click) as mentioned above does not work for me inside of an ion-radio-group

  5. davidseek commented on Apr 26, 2020

    @davidseek

    I don't think that this will get fixed. The behavior is expected, I think.
    If anyone is interested: I "fixed" it, using a button with some css hackery.

    Instead of using actual checkboxes, I used buttons and images of checkboxes.
    Basically something like so:

    <div "myRadioButton" (click)="didClickRadioButton()>
      <img [src]="getImageForRadioButton()">
    </div>
    
    public didClickRadioButton(): void {
     this.isActive = !this.isActive
    }
    
    public getImageForRadioButton(): string {
      return isActive ? "url/to/active/image" : "url/to/deactive/image" 
    }
    

    It's not perfect as it's not using the neat implementation of the in-house
    element of Ionic, but I have been able to have the UI the user needs,
    in a list of hundreds of elements within a virtual scroll list.

    And I don't have to bother about ionChange being fired because of lifecycle.
    It's not a fix, but I hope it helps anyone.

  6. dev-sammy commented on Sep 8, 2021

    @dev-sammy

    Guys I have been into this annoying problem with ion-checkbox when ever I open a new page the the API loads the data and checks the item with 1 or 0 because [(ngModel)] was binding the state to the checkbox. I don't know if it's too late for explanation but I hope this helps someone. I am using Ionic V5 with Angular 11.

    There are few workaround for this given in Github and Ionic forum, the best I can find is

    If you are using the checkbox to call some API or do some asynchronous task which will change the bound value, then

    Don't do this :
    <ion-checkbox [(ngModel)]="item.status" (ionChange)="onCheckItem($event, item)"></ion-checkbox>

    Here the directive would trigger the checked state of the checkbox when the page loads or list is refreshed which will trigger ionChange event again and again infinitely.

    Or this:
    <ion-checkbox [(ngModel)]="item.status" (ngModelChange)="onCheckItem($event, item)"></ion-checkbox>

    Here you are already using the two way binding which is sending instruction to the controller and again you are calling the function with model change event so it will keep calling the function back and forth.

    Or even this:
    <ion-checkbox [ngModel]="item.status" (ngModelChange)="onCheckItem($event, item)"></ion-checkbox>

    This might work for some but it's uncertain as in long lists it might take some time to render the complete lists and some items might get dirty a little late which will change the model and trigger the event when list is refreshed.

    The thing worked for me is:
    <ion-checkbox [checked]="item.status" (ionChange)="onCheckItem($event, item)"></ion-checkbox>

    In this case the checked property will get value as soon as the list is initialized and check the checkboxes but it wouldn't trigger ionChange event until manually checked. But if you want to change the bound value which is item.status when you check or uncheck the checkbox then it will not change as the model is not bound to the checkbox, so you have to change manually inside the function like this:

    onCheckItem(ev: any, item: any) {
     item.status = ev.detail.checked;
    
    //call API or do other stuff
    }
    

    and you can add an extra layer of security by comparing if the item's value and checked state is matched or not inside the function in case any leaked changed detection happens.

    Complete solution:

    <ion-checkbox  [checked]="item.status" (ionChange)="onCheckItem($event, item)"></ion-checkbox>
    
    onCheckItem(ev: any, item: any) {
    if(ev.detail.checked == item.status) return;
     item.status = ev.detail.checked;
    
    //call API or do other stuff
    }
    

    I hope this helps in other Ionic input components as well.

  7. SiegeSailor commented on Dec 7, 2021

    @SiegeSailor

    For anyone who is still being trapped in this issue, I have found a workaround:

    <IonSelect
      okText="OK"
      cancelText="CANCEL"
      slot="end"
      value={status}
      // Key property prevents the first trigger.
      key={status}
      onIonChange={async (event) => {
         const value = event.detail.value;
          // The following prevents the second trigger.
          if (value === status) return;
          // Do something...
      }}
    >
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions