1

I have a question on how two-way binding work in Angular Material checkbox.

This is HTML template from app.component.html:

<div>
  <div *ngFor="let data of testData">
      <mat-checkbox [(ngModel)]="data.isActive" (change)="handleData(data)">{{data.isActive}}</mat-checkbox>
  </div>
</div>

And this is the component (app.component.ts):

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  testData: TestData[];

  ngOnInit() {
    this.testData = [];

    this.testData.push({
      label: 'testData1',
      isActive: false
    });

    this.testData.push({
      label: 'testData2',
      isActive: false
    });
  }

  handleData(data: TestData) {
    console.log(data.isActive);
    data.isActive = false;
  }
}

export class TestData {
  label: string;
  isActive: boolean;
}

All the checkboxes are unchecked by default. When I click a checkbox for the first time, the data parameter in the handleData handler receives true in data.isActive. I set this to false afterwards. The corresponding labels are never changed (they are always false). But I also expect for a checkbox never be checked as well. But it does is checked. Why this happens?

UPDATED I've just added the 'Results' section that contains checkboxes that reflect the ones above:

<div>
  <div *ngFor="let data of testData">
      <mat-checkbox [(ngModel)]="data.isActive" (change)="handleData(data)">{{data.isActive}}</mat-checkbox>
  </div>
</div>

<h2 class="example-h2">Result</h2>

<div>
  <div *ngFor="let data of testData">
    <mat-checkbox [(ngModel)]="data.isActive" (change)="handleData(data)">{{data.isActive}}</mat-checkbox>
  </div>
</div>

Unlike their neighbours above they are never checked.

1 Answer 1

4

The value may be reset too quickly, when Angular has not completed the change detection cycle triggered by the click. It works if we reset the value asynchronously with setTimeout (see this stackblitz):

handleData(data: TestData) {
  console.log(data.isActive);
  setTimeout(() => {
    data.isActive = false;
  });
}

or if we call ChangeDetectofRef.detectChanges() before resetting the value (see this stackblitz):

handleData(data: TestData) {
  console.log(data.isActive);
  this.changeDetectorRef.detectChanges();
  data.isActive = false;
}
Sign up to request clarification or add additional context in comments.

4 Comments

It work with my example too. Is it possible, for example, to subscribe to angular change detector so as not to be dependant on a timeout?
I tried with ChangeDetectorRef.detectChanges first but it does not appear to work. See this stackblitz.
Oh but I found that it does work if we call it before resetting the value. I updated my answer.
You are the greatest!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.