'material angular select all checkbox

I'm trying to implement select all checkbox on angular material. When user click on specifc checkbox (item), master checkbox should show Indeterminate and turn to checked if all checkboxes are selected. Currently I'm having some weird behaviour. Can anyone let me know where I made mistake? Here is stackblitz. Here is my sample code:

app.html

<fieldset class="demo-fieldset">
  <div>
    <mat-checkbox aria-label="Select All" [checked]="isChecked(selected3, itemsObject)" [indeterminate]="isIndeterminate(selected3, itemsObject)" (click)="toggleAll(selected3, itemsObject)">
      Select All list of user (Array of objects) {{isChecked(selected3, itemsObject)}}
    </mat-checkbox>
  </div>
  <div class="demo-select-all-checkboxes" *ngFor="let item of itemsObject">
    <mat-checkbox [checked]="exists(item, selected3)" (click)="toggle(item, selected3)">
      {{ item.val }}
    </mat-checkbox>
    {{exists(item, selected3)}}

  </div>
</fieldset>

app.ts

import {
  Component
} from '@angular/core';

@Component({
  selector: 'app',
  templateUrl: 'app.html',
  styleUrls: ['app.css'],
})
export class CheckboxConfigurableExample {
  itemsObject = [{
    id: 1,
    val: 'john'
  }, {
    id: 2,
    val: 'jane'
  }];
  selected3 = [];

  toggle(item, list) {
    var idx = list.indexOf(item);
    if (idx > -1) {
      list.splice(idx, 1);
    } else {
      list.push(item);
    }
  }

  exists(item, list) {
    return list.indexOf(item) > -1;
  };

  isIndeterminate(x, t) {
    return (x.length !== 0 && x.length !== t.length);
  };

  isChecked(x, t) {
    return x.length === t.length;
  };

  toggleAll(x, t) {
    var l1 = x.length,
      l2 = t.length;
    if (l1 === l2) {
      x.splice(0, l1);
    } else if (l1 === 0 || l1 > 0) {
      //First we need to empty array, because we are using push to fill in array
      x.splice(0, l2);
      t.forEach(y => x.push(y));
    }
  };
}

Here is my stackblitz



Solution 1:[1]

Try this code:

Component:

import {Component} from '@angular/core';
import {

  MatCheckboxChange
} from '@angular/material';

/**
 * @title Configurable checkbox
 */
@Component({
  selector: 'checkbox-configurable-example',
  templateUrl: 'checkbox-configurable-example.html',
  styleUrls: ['checkbox-configurable-example.css'],
})
export class CheckboxConfigurableExample {
  itemsObject = [{
    id: 1,
    val: 'john'
  }, {
    id: 2,
    val: 'jane'
  }];
  selected3 = [];

  toggle(item,event: MatCheckboxChange) {
     if (event.checked) {
      this.selected3.push(item);
    } else {
      const index = this.selected3.indexOf(item);
      if (index >= 0) {
        this.selected3.splice(index, 1);
      }
    }
   console.log(item + "<>", event.checked);
  }

  exists(item) {
    return this.selected3.indexOf(item) > -1;
  };

  isIndeterminate() {
    return (this.selected3.length > 0 && !this.isChecked());
  };

  isChecked() {
    return this.selected3.length === this.itemsObject.length;
  };



  toggleAll(event: MatCheckboxChange) { 

    if ( event.checked ) {

       this.itemsObject.forEach(row => {
          // console.log('checked row', row);
          this.selected3.push(row)
          });

        // console.log('checked here');
    } else {
      // console.log('checked false');
       this.selected3.length = 0 ;
    }
}
}

Template:

<fieldset class="demo-fieldset">
  <div>
     <mat-checkbox aria-label="Select All" [checked]="isChecked()" [indeterminate]="isIndeterminate()" (change)="$event ? toggleAll($event) : null">
      Select All list of user (Array of objects) {{isChecked(selected3)}}
    </mat-checkbox>
  </div>
  <div class="demo-select-all-checkboxes" *ngFor="let item of itemsObject">
    <mat-checkbox (click)="$event.stopPropagation()"
                    (change)="$event ? toggle(item, $event) : null"
                    [checked]="exists(item)">
      {{ item.val }}
    </mat-checkbox>
    {{exists(item)}}

  </div>
</fieldset>

Solution 2:[2]

A different strategy might be to bind to values on an object and component, instead of calling methods. This way, you may be able to manage the state more effectively in your component.

For example, you could introduce the following in your object model:

public itemsObject = [{
  id: 1,
  val: 'john',
  isChecked: false
}, {
  id: 2,
  val: 'jane',
  isChecked: false
}];

You can then bind this to the checkboxes using:

[checked]="item.isChecked"

Binding the the "change" event will also let you know when things are changed, and you can then act accordingly:

<mat-checkbox [checked]="item.isChecked" (change)="itemChanged(item,$event)">

I created a Stackblitz which shows a working example:-

https://stackblitz.com/edit/angular-uuw7qh-ninwen

Solution 3:[3]

In your checkbox-configurable-example.html you use:

[checked] = "isChecked(selected3, itemsObject)"

I simply changed this to:

value="isChecked(selected3, itemsObject)"

And it looks as you expect? I suspect some more tweaking is needed for the overall project, but this might get you in the direction?

Solution 4:[4]

On your Html, Add something like below:

<table class="table">
              <thead>
                <tr>
                  <th>
                    <mat-checkbox (change)="onChangeSelectAll($event)">Select/Deselect All</mat-checkbox>
                  </th>

                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let item of offers">
                  <td>
                    <mat-checkbox [checked]="item.selected" (change)="selection($event, i, item)" name="chkInvoice"></mat-checkbox>
                  </td>                      
                </tr>
              </tbody>
            </table>

ON your typescript file,

onChangeSelectAll(event) {
    if (event.checked) {
        this.offers.forEach(obj => {
            obj.selected = true;
        });
    }
    else {
        this.offers.forEach(obj => {
            obj.selected = false;
        });
    }
}

Solution 5:[5]

Another version without using selected3 array

.html file

<fieldset class="demo-fieldset">
    <div>
        <mat-checkbox aria-label="Select All" [checked]="isChecked()" [indeterminate]="isIndeterminate()"
            (change)="toggleAll($event)">
            Select All list of user (Array of objects) {{isChecked()}}
        </mat-checkbox>
    </div>
    <div class="demo-select-all-checkboxes" *ngFor="let item of itemsObject">
        <mat-checkbox [checked]="item.selected" (change)="toggle(item)">
            {{ item.val }}
        </mat-checkbox>
        {{exists(item)}}
    </div>
</fieldset>

.ts file

Added 'selected' key to itemsObject for binding checkbox value. Define itemsObject and allCheckboxSelected.

 itemsObject = [
    {
      id: 1,
      val: 'john',
      selected: false
    },
    {
      id: 2,
      val: 'jane',
      selected: false
    }
  ];
  allCheckboxSelected;


toggleAll(event: MatCheckboxChange) {
    if (event.checked) {
      this.itemsObject.forEach(element => {
        element.selected = true;
      });
    } else {
      this.itemsObject.forEach(element => {
        element.selected = false;
      });
    }
  }

  isChecked() {
    this.allCheckboxSelected =
      this.itemsObject.length != 0 && this.itemsObject.every(el => el.selected);
    return this.allCheckboxSelected;
  }

  isIndeterminate() {
    if (this.itemsObject.length === 0) {
      return false;
    }
    return (
      this.itemsObject.some(el => el.selected) && !this.allCheckboxSelected
    );
  }

  toggle(checkbox) {
    let index = this.itemsObject.findIndex(el => el.id === checkbox.id);
    this.itemsObject[index].selected = !this.itemsObject[index].selected;
  }

  exists(item) {
    let index = this.itemsObject.findIndex(el => el.id === item.id);
    return this.itemsObject[index].selected;
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 suhailvs
Solution 2 Røye
Solution 3 Skdy
Solution 4 Abdus Salam Azad
Solution 5 sajiyya k