Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set indeterminate on Angular checkbox

I'm trying to programatically set the value of Angular checkboxes to either, false true or indeterminate. I understand that I cannot set the checkbox to the value indeterminate however we do have access to [indeterminate] on the input. Is it possible to set all three states via ngModel somehow?

I have the following code which kinda works, however I am getting a ExpressionChangedAfterItHasBeenCheckedError error.

HTML

<div *ngFor="let label of ClientLabels | async">
  <label for="{{label.objectId}}">{{label.labelName}}</label>
  <input id="{{label.objectId}}" type="checkbox" name="group" [indeterminate]="checkedLabels()"
      [checked]="checkedLabels(label)" (change)="toggleSelectedLabels(label)" />
</div>

TS

checkedLabels(label): boolean {
    const index = this.selectedLabels.indexOf(label.objectId);
    this. indeterminateState = false;
    if (!this.selectedClients.length) {
      return false;
    }
    if (this.countInArray(this.selectedLabels, label.objectId) === this.selectedClients.length) {
      return true;
    }
    if (index >= 0) {
      this. indeterminateState = true;
    }
  }

countInArray(array, value) {
  return array.reduce((n, x) => n + (x === value), 0);
}

Here the use case is similar to that of labels in Gmail, except clients are used in the place of an email. If all of the emails have the same label then they show as checked, however if not all of them share the label then it will show an indeterminate which can be cycled through the three states (true, false, indeterminate).

Q1. How can I cycle through these three state in the same way as you can with gmail?

Q2. Why am I getting the ExpressionChangedAfterItHasBeenCheckedError with the current setup?

Here is a Stackblitz of current progress https://stackblitz.com/edit/angular-3bbutx

Example of Gmail Tri-State checkbox

like image 945
Taylorsuk Avatar asked Jul 16 '26 03:07

Taylorsuk


2 Answers

To make a checkbox indeterminated, you can use a directive

import { Directive, ElementRef,Input } from '@angular/core';

@Directive({ selector: '[indeterminate]' })
export class IndeterminateDirective {
   @Input() 
   set indeterminate(value)
   {
     this.elem.nativeElement.indeterminate=value;
   }
    constructor(private elem: ElementRef) {
    }
}

Then you're checkbox can be like

<input class="pull-left" type="checkbox" 
     [indeterminate]="client.indeterminated" 
     [checked]="client.checked" (click)="click(client)"/>

where

  click(cliente: any) {
    let indeterminated=(!cliente.checked && !cliente.indeterminated) ? true : false;
    let checked=(!cliente.checked && cliente.indeterminated)?true:false
    cliente.indeterminated = indeterminated;
    cliente.checked=checked;
  }

See that you have two variables "checked" and "indeterminated", you can make a cycle as you want

like image 121
Eliseo Avatar answered Jul 17 '26 19:07

Eliseo


Yes, you can use [indeterminate]="condition" (without any directives). You can't do indeterminate="{{condition}}". The difference is that [...] sets property of the element, while without brackets you are setting attribute. HTMLInput element has property indeterminate, but doesn't have such attribute.

In order use ngModel or FormControl, which holds simultaneously the both checked and indeterminate states, you should create a custom directive or/and additional logic in your primary component (for reference, see the answer from @Eliseo).

Q1. How can I cycle through these three state in the same way as you can with gmail?

Handle clicks, change state of the pair "checked" and "indeterminate" in cycle. Example with jQuery.

Q2. Why am I getting the ExpressionChangedAfterItHasBeenCheckedError with the current setup?

You have side effect in the checkedLabels method, which changes state of the component, each time Angular checks value of the expression [indeterminate]="checkedLabels()".

like image 44
user2447143 Avatar answered Jul 17 '26 19:07

user2447143



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!