Binda ngModel till dynamisk kryssrutelista: Angular 2/Typescript


Jag är inte säker på det korrekta sättet att binda och uppdatera en modell där kryssrutorna genereras dynamiskt. (Detta är ett ASP.NET Core-projekt med Angular 2 som initialt skapades med Yeoman-generatorn) Eller en enkel kryssruta för den delen har jag just upptäckt.

Nedan är en nedskalad kod som har en anläggning och tidsluckor. Varje anläggning kan ha flera tidsluckor. Tidsluckornas kryssrutelista visas korrekt. De ursprungliga uppgifterna anger att endast en tidslucka ska vara markerad. Men när jag laddar en anläggning är alla tidsluckor markerade. De binds inte med ngModel som jag förväntade mig.

  1. Hur åtgärdar jag det så att endast de tidsluckor som ingår i anläggningsdatan kontrolleras istället för alla?
  2. Hur binder jag det som är markerat till ngModel timeslotids-egenskapen? Behöver jag skapa en array-egenskap på komponenten och manipulera den istället för att förlita mig på ngModel? (provade detta men gjorde det uppenbarligen inte rätt så återgick till koden nedan)
  3. Jag verkar också ha problem med att binda till en enkel kryssruta (haselectric) eftersom denna också inte markeras när den borde. Saknar jag ett import som skulle lösa allt detta?

KODEXPLANATION Jag har två objekt (facility och timeslot) som kommer från ett API som jag binder till gränssnitt. De har kraftigt reducerats i egenskaper för enkelhetens skull:

export interface IFacility {   
   id: number,
   name: string,
   haselectric: boolean, 
   timeslotids: number[],    
   timeslots: ITimeslot[]

}
export interface ITimeslot {
    id: number,
    timeslotname: string    
}

Detta är JSON-data för Tidsluckor:

[{"id":1,"timeslotname":"Daily"},{"id":2,"timeslotname":"Hourly"},{"id":4,"timeslotname":"Market"}]

Detta är JSON-data för en enskild anläggning innan den uppdateras:

{"id":2,"name":"Clements Building","haselectric":true,"timeslotids":[1],"timeslots":[{"id":1,"timeslotname":"Daily"}]}

Komponent för redigering av en anläggning (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>

Kod för komponenten (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. Anledningen till att jag har två egenskaper i facility-objektet för tidsluckor är att en lista med ID:n är mycket enklare att skicka vidare till API:et för uppdatering. Istället för att skicka fullständiga modeller (tidsluckor är större än vad jag har här och behövs inte för att uppdatera databasen). Vänligen reservera kommentarer om det då det är orelaterat till vad som behöver åstadkommas.

Jag hittade denna fråga... men tyvärr svarade ingen den stackars personen: ngmodel-bindning med dynamisk array av kryssrutor i angular2 Försökte detta men det fungerade inte: Hämta värden från en dynamisk kryssrutelist Jag försökte också en version av att uppdatera en lokal egenskap vid (change) av kryssrutan men det verkade inte fungera heller: Angular 2: Hämta värden på flera markerade kryssrutor

Svar #1

Baserat på UFO:s svar ovan kunde jag lista ut lösningen på min fråga. Tack, UFO.

<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>

Sedan funktionen på komponenten:

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);    
}

Svar #2

Jag har en kod liknande din (ngFor över alla möjliga kryssrutor, några av dem ska vara markerade, ändringar ska sparas i någon datastruktur som vi inte itererar över), och här är vad jag kommer fram till:

<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>

Här är 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);
    }
  }
}

Dessutom tror jag att det borde vara event.target, men det är event.source på grund av materialet. Ganska klumpig lösning, men jag tror att det kommer hjälpa dig att lista ut hur du kan uppnå det du vill.

Relaterade artiklar