Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular mat-checkbox dynamically with ngModel

I have a class Offer which have one property "units" as array of objects.

export class Offer {
   public propertyA: string;
   public propertyB: string;
   public units: Unit[];
}

export class Unit {    
    public code: string,
    public name: string,
    public checked: boolean
}

I am using Angular2, these units must be checkboxes the user can select. Also I am using angular material.

The html code looks like:

<div *ngFor="let unit of model.units; let i=index">
  <mat-checkbox [(ngModel)]="model.units[i].checked"
    id="units[{{i}}]" name="units[{{i}}]">
       {{ unit.name }}
  </mat-checkbox>                        
</div>    

The units property is loaded using:

this.model.units.push(new Unit("code1","name1", false));
this.model.units.push(new Unit("code2","name2", false));
this.model.units.push(new Unit("code3","name3", false));

When I send the form the checked property does not contains the checked value.

What am I doing wrong??

like image 305
aldo Avatar asked Nov 20 '17 02:11

aldo


People also ask

How do I keep my mat checkbox checked by default?

To set mat-checkbox checked by default we use checked attribute or [ngModel] as shown below. We can set the IsChecked property to true in constructor.

How do you checked mat checkbox in angular?

Angular Material can be checked dynamically using checked property of <mat-checkbox> element. Checkbox can also be checked using Angular ngModel . The ngModel can be used for two-way binding. Angular Material <mat-checkbox> has change event that is emitted when checkbox is checked and unchecked.


3 Answers

Add [checked]="unit.checked" and remove ngModel, id and name from your mat-checkbox. This does one way binding when you load the page but to make the two way binding work you need to do something similar like this which i have done in my project:

Pass the $event on change and set the value in the component file manually:

Change your HTML to:

<div *ngFor="let unit of model.units">
  <mat-checkbox [checked]="unit.checked" 
  (change)="valueChange(model.units,unit,$event)">
       {{ unit.name }}
  </mat-checkbox>                        
</div>

Add this to Component file:

valueChange(model.units, unit, $event) {
        //set the two-way binding here for the specific unit with the event
        model.units[unit].checked = $event.checked;
    }

EDIT/UPDATE: Found a solution recently for two way binding without handling it explicitly

Make sure model.units have a property for checked. This is the easiest way to add it:

component.ts:

this.model.units.forEach(item => {
                       item.checked = false; //change as per your requirement
                    });

html:

 <div *ngFor="let unit of model.units;let i=index">
    <mat-checkbox [(ngModel)]="unit.checked" [checked]="unit.checked"                                                                                    
    name="{{i}}-name">  //make sure name is unique for every item                             
    </mat-checkbox>
</div>

If you want to control the enable/disable options as well try adding this:

component.ts:

this.model.units.forEach(item => {
                       item.checked = false;//change as per your requirement
                       item.disabled = true;//change as per your requirement
                        });

html:

<div *ngFor="let unit of model.units;leti=index">
        <mat-checkbox [(ngModel)]="unit.checked" [checked]="unit.checked"
         [disabled]="unit.disabled"                                                                                    
       name="{{i}}-name">//make sure name is unique for every item
        </mat-checkbox>
    </div>
like image 129
hakuna Avatar answered Oct 09 '22 21:10

hakuna


This work for me tested in Angular 7.

Just to make sure that name is unique for each checkbox

<mat-grid-list cols="4" rowHeight="100px">
                    <mat-grid-tile *ngFor="let item of aspNetRolesClaims" [colspan]="1" [rowspan]="1">
                        <span>{{item.claimValue}}</span>
                        <mat-checkbox [(ngModel)]="item.claimValue" name="{{item.claimType}}">{{item.claimType}}</mat-checkbox>
                    </mat-grid-tile>
                </mat-grid-list>
like image 23
San Jaisy Avatar answered Oct 09 '22 23:10

San Jaisy


I tested in Angular 7 and I couldn't find any error given your question and you can see a working demo in this StackBlitz example, but here goes my answer.

In order to work you gotta give an unique name to your ngModel binded input.

Example taking your code:

<div *ngFor="let unit of model.units; let i=index">
  <mat-checkbox 
    [(ngModel)]="model.units[i].checked"
    id="units-{{i}}" 
    name="units-{{i}}" // using plain slug-case instead of array notation
  >
      {{ unit.name }}
  </mat-checkbox>                        
</div> 

I prefer not to use the array notation in these cases (units[{{i}}]), because it means that all fields are joined in the same thing, but as your code seems to work this is just a preference and not the cause of the error.

like image 4
Machado Avatar answered Oct 09 '22 22:10

Machado