I'm not sure the proper way to bind and update a model where the checkboxes are dynamically generated. (This is a ASP.NET Core project with Angular 2 initially created using the Yeoman generator) Or a simple checkbox for that matter I have just found out.
Below is stripped down code, which has a facility and timeslots. Each facility can have multiple timeslots. The timeslot checkbox list displays fine. The initial data indicates only one timeslot should be checked. But when I load up a facility, all the timeslots are checked. They aren't binding with ngModel as I expected.
CODE EXPLANATION I have two objects (facility and timeslot) coming from an api which I bind to interfaces. They have been significantly reduced in properties for simplicity's sake:
export interface IFacility {
id: number,
name: string,
haselectric: boolean,
timeslotids: number[],
timeslots: ITimeslot[]
}
export interface ITimeslot {
id: number,
timeslotname: string
}
This is the JSON data for Timeslots:
[{"id":1,"timeslotname":"Daily"},{"id":2,"timeslotname":"Hourly"},{"id":4,"timeslotname":"Market"}]
This is the JSON data for a single Facility prior to being updated:
{"id":2,"name":"Clements Building","haselectric":true,"timeslotids":[1],"timeslots":[{"id":1,"timeslotname":"Daily"}]}
Component for editing a facility (facility.component.html):
<form #form="ngForm" (ngSubmit)="submitForm()" *ngIf="formactive">
<input type="hidden" [(ngModel)]="updatedfacility.id" #id="ngModel" name="id" />
<div class="form-group">
<label for="name">Facility Name *</label>
<input type="text" class="form-control input-lg" [(ngModel)]="updatedfacility.name" #name="ngModel" name="name" placeholder="Facility Name *">
</div>
<div class="form-group">
<label for="exhibitspaces">Has Electric *</label>
<input type="checkbox" class="form-control input-lg" [(ngModel)]="updatedfacility.haselecric" #haselectric="ngModel" name="haselectric">
</div>
<div class="form-group">
<label for="timeslots">Select Timeslots Available for Rent *</label>
<div *ngIf="dbtimeslots" >
<span *ngFor="let timeslot of dbtimeslots" class="checkbox">
<label for="timeslot">
<input type="checkbox" [(ngModel)]="updatedfacility.timeslotids" value="{{timeslot.id}}" name="{{timeslot.id}}" [checked]="updatedfacility.timeslotids.indexOf(timeslot.id)" />{{timeslot.timeslotname}}
</label>
</span>
</div>
</div>
<button type="submit" class="btn btn-lg btn-default" [disabled]="form.invalid">Update</button>
</form>
Code for the component (facility.component.ts)
import { Component, OnInit, Input, Output, OnChanges, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ActivatedRoute, Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { FacilityService } from '../../../services/facility.service';
import { TimeslotService } from '../../../services/timeslot.service';
import { IFacility } from '../../../services/facility.interface';
import { ITimeslot } from '../../../services/timeslot.interface';
@Component({
template: require('./facility.component.html')
})
export class FacilityDetailsComponent {
facility: IFacility;
httpresponse: string;
successMessage: string;
errormessage: string;
showSuccess: boolean = true;
showError: boolean = true;
formactive: boolean = true;
dbtimeslots: ITimeslot[];
constructor(
private _route: ActivatedRoute,
private _router: Router,
private _facilityservice: FacilityService,
private _timeslotservice: TimeslotService
) {}
ngOnInit(): void {
this._timeslotservice.getTimeslots().subscribe(timeslots => this.dbtimeslots = timeslots, error => this.errormessage = <any>error);
let id = +this._route.snapshot.params['id'];
this._facilityservice.getFacility(id)
.subscribe(facility => this.facility = facility,
error => this.errormessage = <any>error);
}
submitForm() {
//update the facility through the service call
this._facilityservice.updateFacility(this.facility)
.subscribe(response => {
this.httpresponse = response;
console.log(this.httpresponse);
this.successMessage = "Facility updated!";
this.formactive = false;
setTimeout(() => this.formactive = true, 3);
this.showSuccess = false;
},
error => {
this.errormessage = <any>error;
this.showError = false;
});
}
}
P.S. The reason I have a two properties in the facility object for timeslots is because a list of id's is much easier to pass through to the API for updating. Rather than passing full models (timeslots is larger than what I have here and not needed to update the database). Please reserve comments on that as it's unrelated to what needs to be accomplished.
I found this question...but sadly, no one answered this poor fellow: ngmodel binding with dynamic array of checkbox in angular2 Tried this but didn't work: Get values from a dynamic checkbox list I also tried version of updating an local property on (change) of the checkbox but that didn't seem to work either: Angular 2: Get Values of Multiple Checked Checkboxes
Based on НЛО answer above I was able to figure out the solution to my question. Thank you НЛО.
<div class="form-group">
<label for="timeslots">Select Timeslots Available for Rent *</label>
<div *ngIf="dbtimeslots">
<span *ngFor="let timeslot of dbtimeslots" class="checkbox">
<label>
<input type="checkbox" value="{{timeslot.id}}" name="{{timeslot.id}}" [checked]="(facility.timeslotids && (-1 !== facility.timeslotids.indexOf(timeslot.id)) ? 'checked' : '')" (change) ="updateSelectedTimeslots($event)" />{{timeslot.timeslotname}}
</label>
</span>
</div>
</div>
Then the function on the component:
updateSelectedTimeslots(event) {
if (event.target.checked) {
if (this.facility.timeslotids.indexOf(parseInt(event.target.name)) < 0) {
this.facility.timeslotids.push(parseInt(event.target.name));
}
} else {
if (this.facility.timeslotids.indexOf(parseInt(event.target.name)) > -1)
{
this.facility.timeslotids.splice(this.facility.timeslotids.indexOf(parseInt(event.target.name)), 1);
}
}
//console.log("TimeslotIDs: ", this.facility.timeslotids);
}
I have a code similar to yours (ngFor
over all of the possible checkboxes, some of them should be checked, changes are to be saved in some data structure we are not iterating over), and here's what I come up with:
<md-checkbox
*ngFor="let some of availableSomes"
name="{{ some }}"
checked="{{ data.somes && -1 !== this.data.somes.indexOf(some) ? 'checked' : '' }}"
(change)="updateSome($event)">
{{ some }}
</md-checkbox>
Here's updateSome
:
updateSome(event) {
if (-1 !== this.availableSkills.indexOf(event.source.name)) {
if (event.checked) {
this.data.somes.push(event.source.name);
} else {
this.data.somes.splice(this.data.somes.indexOf(event.source.name), 1);
}
}
}
Also, I believe it should be event.target
, but it's event.source
due to material. Kind of clumsy solution, but I think it will help you figure out how can you achieve what you want
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With