Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

state of the checkbox not getting updated even after its ng-model value is updated #9275

Open
1 task
IamKritika opened this issue Jun 16, 2016 · 45 comments
Open
1 task

Comments

@IamKritika
Copy link

  • I'm submitting a bug for state of the checkbox not getting updated even after its ng-model value is updated
  • bug report

Current behavior
If you try and update the ngModel of a checkbox in say ngModelChange method, the ngModel value is updated but the checkbox still remains in previous state.

Expected/desired behavior
The state of checkbox should change as ngModel value changes.

  • What is the motivation / use case for changing the behavior?
    I am trying to implement "select all" checkbox through angular 2.

I have a list of checkboxes and one select all checkbox. And in my use case it's so that if user checks all checkboxes from the list manually , I have to uncheck all the checkboxes from the list and check the "All" checkbox. So I have binded a ngModel and ngModelChange to all the checkboxes .Now what happens is if I try to update the ngModel in the ngModelChange method ,the state of checkbox is not changed even though the ngModel value is updated (since angular2 listens to events only and not to change in value of ngModel). I have tried to achieve this through the attribute directive as well but nothing seems to work.

  • Angular version: 2.0.0-beta.X
  • Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
  • Language: [all | TypeScript X.X | ES6/7 | ES5 | Dart]
@zoechi
Copy link
Contributor

zoechi commented Jun 16, 2016

Please provide a plunker that allows to reproduce.

@IamKritika
Copy link
Author

plunker

Steps:

  1. Try selecting all the checkboxes except "All" manually.
  2. Now you can observe that the ngModel value shown along gets updated but state of last selected checkbox is not updated.

I have updated the ngModel in the ngModelChange method but its not getting reflected.

@zoechi
Copy link
Contributor

zoechi commented Jun 17, 2016

There is a lot of code I'm sure could be removed and the problem still be reproduced.
I added setTimeout() and it worked (not beautiful of course). The updates are somehow conflicting but I don't want to spend my time to understand what the logic of your code is to get to the core of the problem.

I think one of the channels mentioned in CONTRIBUTING - Got a Question or Problem? should be used instead.

@IamKritika
Copy link
Author

Updated Plunker with simplified logic
plunker

The problem I am facing is -
I am unable to update the state of the checkbox even if its ngModel is updated. Its not specific to this example, everyone will face the same issue if they try to reset the state of checkboxes that user has checked to make a selection. And setTimeout() is not the way out to update the state of checkboxes (we cannot use it in real time applications for every state update we want).

@zoechi
Copy link
Contributor

zoechi commented Jun 17, 2016

@IamKritika seems you didn't save the changes in the plunker, it's the same content as the first one.

@IamKritika
Copy link
Author

@zoechi can you check it now.. It seems to work fine for me.

@zoechi
Copy link
Contributor

zoechi commented Jun 17, 2016

I can confirm that this is fishy.
My reduced repo http://plnkr.co/edit/WGVZaRq4fyiAHXsD84BO?p=preview

What's weird is that instead of setTimeout() calling ChangeDetectorRef.detectChanges() before changing the value also works. detectChanges doesn't work when called afterwards.

@kuldeepkeshwar
Copy link

kuldeepkeshwar commented Jun 17, 2016

@zoechi
is using ChangeDetectorRef.detectChanges() or setTimeout() right way of doing it or this is just a workaround ?
angular should provide this out of the box itself.

@zoechi
Copy link
Contributor

zoechi commented Jun 17, 2016

I think this is caused by change detection not seeing a change if the value is changed and immediately restored before change detection has a chance to run and detect the change. This is the reason the "new" value is not propagated back to the checkbox. The checkbox updates itself and Angular doesn't see a change that would make it necessary to propagate back to the checkbox.

If you explicitly invoke change detection before the value is reset, then change detection kicks in (same with setTimeout())

I think this is by design.

@IamKritika
Copy link
Author

Yes that's absolutely correct that angular doesn't see any change and hence the state of checkbox is unaltered . But then resetting the state of any ngModel would not be possible without explicitly invoking change detection (which I feel is a overhead) .
Angular should have a provision to handle such use cases.

@Andikki
Copy link

Andikki commented Jun 23, 2016

I created a plunker illustrating the problem and an alternative solution using Controls for binding.
http://plnkr.co/edit/8NIMmV?p=preview

It still would be nice if [ngModel]+(event) binding worked, because it means you can have only one event-handling method in the component for all the checkboxes in a list, while with Controls you have to create one per checkbox and subscribe to each of them.

@IamKritika
Copy link
Author

@Andikki plunker link is not opening. Can you share the correct url ?

@Andikki
Copy link

Andikki commented Jun 23, 2016

@IamKritika Sorry, markdown failure on my part. Fixed.

@kara kara removed this from the 2.0.0-rc.4 milestone Jun 23, 2016
@thejoecode
Copy link

Ran into something similar today. (change) is passed the state of the checkbox when it was clicked, not the new state. All the examples I see use $event to get around this issue. Click the checkbox a few times, expect the value [(ngModel)] is bound to and (change) to be in sync. They are not.

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

@Andikki
Copy link

Andikki commented Jun 25, 2016

@thejoecode Just use (ngModelChange) instead of (change)

@hxlniada
Copy link

hxlniada commented Jul 9, 2016

Facing the same problem today. @zoechi Do you think it's not a bug?

@zoechi
Copy link
Contributor

zoechi commented Jul 9, 2016

@thejoecode looks like #3406

@awerlang
Copy link
Contributor

@thejoecode see #10038 (comment)

@ghost
Copy link

ghost commented Oct 17, 2016

even I had this issue --> replaced (click) event with (ngModelChange) event. Now its working fine.

@ShubhK
Copy link

ShubhK commented May 31, 2018

I am also facing the issue related to checkbox list. i have to maintain the checkbox list checked on back from browser. But it is not working. please help me to resolve this issue.

@vildantursic
Copy link

Hi all,
I am having this issue with checkbox after I moved project to angular 6.0.3
<input type="checkbox"> there is nothing bind to this checkbox, but it is not working for some reason, can't be checked

peek 2018-06-07 10-12

Same happened with form submit button and radio buttons.

Thanks in advance

@zoechi
Copy link
Contributor

zoechi commented Jun 7, 2018

@vildantursic that does look like a different issue. This issue is quite old and not introduced with 6.0.3
I'd suggest to create a new issue with StackBlitz/Plunker example to reproduce.

@vildantursic
Copy link

@zoechi Thank you, I solved it somehow :)

I have to share what caused problem
I don't know why but this (click)="isOpenedSearchByCode = false" caused my check-boxes and radio-buttons to stop work.
it was on div element above my <router-outlet></router-outlet> element

Should I report this?

@tytskyi
Copy link

tytskyi commented Jun 16, 2018

@vildantursic remember that returning false from event listener in javascript makes the same effect as executing event.preventDefault().
Safer to wrap logic, even such simple in void method, and then use in in template.

@bluefire2121
Copy link

bluefire2121 commented Jan 16, 2019

This took me forever to understand, but I finally fixed the issue.
In my case, I was attempting to change the mat-selection-list items by pushing and popping items from an array. Angular's Change Detection isn't triggered on array additions or subtractions. The array needs a new instance.
Using array destructing triggers the change event.

this.myArr = [...this.myArr];

Angular ChangeDetector: ChangeDetectorRef

CPI's answer helped on stackoverflow helped.

@kgish
Copy link

kgish commented Apr 30, 2019

Hi all,
I am having this issue with checkbox after I moved project to angular 6.0.3
<input type="checkbox"> there is nothing bind to this checkbox, but it is not working for some reason, can't be checked

peek 2018-06-07 10-12

Same happened with form submit button and radio buttons.

Thanks in advance

Still broken for Angular 7.

@yuliankarapetkov
Copy link

yuliankarapetkov commented Jan 31, 2020

Holy cow! It's 2020, Angular 9 is just around the corner and I am still experiencing this issue 😱

Decided to use a custom component that looks like a checkbox but uses a button underneath.

@rasel-rz
Copy link

i am still having the issue. Can anyone provide a solution or a workaround? Even when i inspect the Checkbox, the ng-reflect-checked value is False but still showing theTickon the display.

@HighSoftWare96
Copy link

Guys, why this is still not working in Angular 8.2? It has to be fixed...
Is there at least a workaround?

@rocks6
Copy link

rocks6 commented May 8, 2020

Having this issue also

EDIT: Fixed by using the (change) event instead of (click)

@calvarezd55
Copy link

calvarezd55 commented Mar 4, 2021

Hi guys, i had a similar issue. I had some checkbox and one for check all others one.

When i clicked over some checkbox it change its state properly but when i clicked over checkAll checkbox the above checkboxes did not change their state.

I had to import jquery on my angular component (maybe is not the best solution but it works for me)
import * as jquery from 'jquery';

and then my checkAll function was something like that, i had to fire the change event with trigger() function to change the checkbox state

checkAll(checked: boolean) {
    this.checks.forEach((element: ElementRef) => {
      jquery(`#${element.nativeElement.id}`).prop("checked",checked).trigger("change");
    });
  }

My general checkbox is:
<input type="checkbox" (change)="checkAll($event.target.checked)">

And the others checkboxes

<div *ngFor="let product of products">
<input id="check_{{product.sku}}" (change)="changeProductCheck($event, product.sku)"  type="checkbox" #checks>
</div>

i used ViewChildren on my component for loop my checkboxes
@ViewChildren("checks") checks: QueryList<ElementRef>;

@salarx
Copy link

salarx commented Aug 27, 2021

After spending 2 hours scratching my head, I have finally found a workaround:
Plunker

Change this:
<input type="checkbox" [(ngModel)]="location.status" (ngModelChange)="onLocationSelect(location,location.status)"/>

To this:
<input type="checkbox" [ngModel]="location.status" (click)="toggleStatus(i)" (ngModelChange)="onLocationSelect(location,location.status)"/>

toggleStatus function:
toggleStatus(i) { this.locationList[i].status = !this.locationList[i].status; }

Also solves #3406

Explanation:

Though I'm not familiar how Angular handles input (checkbox in this case) state rendering, but this gives some idea:

Angular would only change the state of a checkbox (ticked, unticked) when it detects a change in variable state (true, false in this case). The initial and final variable states of the third checked checkbox, both are false. Though (ngModel) momentarily sets it to true, (ngModelChange) again sets it to false. (ngModelChange) executes immediately after (ngModel) (consider it as a single event), and after that checkbox state is rendered. Since Angular detects no change in the variable state ((false => false)), it keeps the state of the checkbox as it is (the ticked state).

Workaround:
(click) event executes before (ngModel) (and hence before (ngModelChange)). (click) changes the variable state, then [ngModel] binds the changed property, also changing the checkbox state. Since there is change in model, (ngModelChange) is executed, which in our case changes the variable state again, followed by [ngModel] which binds the changed property (again changing the variable state). So the third checkbox will be checked, followed by all 3 checkboxes being unchecked.

@onewland-q
Copy link

If y'all are having issues with the click event conflicting with your calculated [checked] values, I'd recommend checking out this provider: { provide: MAT_CHECKBOX_DEFAULT_OPTIONS, useValue: { clickAction: 'noop' } as MatCheckboxDefaultOptions }, which gives you better control of exactly what happens on a click.

@FreeBLD
Copy link

FreeBLD commented Jan 10, 2022

If y'all are having issues with the click event conflicting with your calculated [checked] values, I'd recommend checking out this provider: { provide: MAT_CHECKBOX_DEFAULT_OPTIONS, useValue: { clickAction: 'noop' } as MatCheckboxDefaultOptions }, which gives you better control of exactly what happens on a click.

Thanks, actually that really solved my issues. I was forced to use the vanilla (click) event for my changes as I was content projecting the checkbox while the parent element had another (click) event registered on itself. Using the Angular material API (changes) on the checkbox element would not trigger properly as this post already extensively discussed. I implemented the provider on the component itself via component decorator to localize. Applying this globally in the Material Module would break default functionality if you have checkboxes with (change) implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests