反应形式-带有来自API的数据的预选下拉选项

时间:2019-01-08 20:16:41

标签: angular angular-reactive-forms

在Angular 7中,我有一个反应式表单,该表单用于创建和编辑现有对象。创建效果很好。

编辑时,API调用返回一个用于初始化FormGroup的对象。其中一些属性是下拉选择的结果。如何预先选择带有FormGroup数据的下拉菜单?

在示例中,我模拟了我们正在编辑的面板或订单项,它来自“服务器”,该面板具有承印物属性。可以将基材想像成汽车。为了准确了解汽车(Tundra SR5),您首先必须选择品牌(Toyota),然后选择饰件(SR5)。同样,基板具有两个定义属性,即尺寸(4x8)和厚度(0.75)。

所需的效果是,在加载表格时,下拉菜单应该已经具有正确的大小,厚度和在下拉菜单中选择的与基板在“服务器”上匹配的基材

我创建了带有功能简化版本的StackBlitz。

StackBlitz:https://stackblitz.com/edit/angular-sfusar

component.html

<form [formGroup]="panelForm" *ngIf="!loading">
  <section class="form-block">
    <div class="form-group">
      <label for="select_size">Size</label>
      <div class="select">
        <select id="select_size" name="select_size" formControlName="size" (ngModelChange)="sizeChange($event)"
            required>
          <option *ngFor="let s of sizes" [ngValue]="s">{{ s.width }} x {{ s.height }}</option>
        </select>
      </div>
    </div>

    <div class="form-group">
      <label for="select_thickness">Thickness</label>
      <div class="select">
        <select id="select_thickness" name="select_thickness" formControlName="thickness" (ngModelChange)="thicknessChange($event)"
            required>
          <option *ngFor="let t of thicknesses" [ngValue]="t">{{t.value}}</option>
        </select>
      </div>
    </div>

    <div class="form-group">
      <label for="select_substrate">Material</label>
      <div class="select">
        <select id="select_substrate" name="select_substrate" formControlName="substrate"
            required>
          <option *ngFor="let s of substrates" [ngValue]="s">{{s.name}}</option>
        </select>
      </div>
    </div>
  </section>
</form>

component.ts

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

import { OrderItem } from '../classes/order-item';
import { Size } from '../classes/size';
import { Thickness } from '../classes/thickness';
import { SubstrateService } from '../services/substrate.service';
import { OrderItemService } from '../services/order-item.service';
import { SizeService } from '../services/size.service';
import { ThicknessService } from '../services/thickness.service';
import { Substrate } from '../classes/substrate';
import {
  FormGroup,
  FormControl,
  Validators,
  FormArray,
  FormBuilder
} from '@angular/forms';

@Component({
  selector: 'app-order-item-form',
  templateUrl: './order-item-form.component.html',
  styleUrls: ['./order-item-form.component.scss']
})
export class OrderItemFormComponent implements OnInit {        
  panel: OrderItem;
  panelId: number;
  loading: boolean;
  isEdit: boolean;

  sizes = new Array<Size>();
  thicknesses = new Array<Thickness>();
  substrates = new Array<Substrate>();

  selectedSize: Size;
  selectedThickness: Thickness;

  panelForm: FormGroup;

  constructor(
    private substrateService: SubstrateService,
    private sizeService: SizeService,
    private thicknessService: ThicknessService,
    private orderItemService: OrderItemService,
    private fb: FormBuilder
  ) {}

  ngOnInit() {
    this.loading = true;

    // set this to === 1 to invoke the "Edit" panel form and isEdit === true
    this.panelId = 1;

    const sizePromise = this.sizeService.getList().toPromise();
    const thicknessPromise = this.thicknessService.getList().toPromise();

    Promise.all([
      sizePromise,
      thicknessPromise
    ]).then(response => {
        this.sizes = response[0];
        this.thicknesses = response[1];

        this.isEdit = this.panel != null;

        this.orderItemService.getSingle(this.panelId).subscribe(response => {
          this.panel = response;
          this.buildForms();

          console.log(this.panel);
          this.loading = false;
        });        
    });
  }

  buildForms() {
    this.panelForm = new FormGroup({
      size: new FormControl(this.panel.substrate.size, Validators.required),
      thickness: new FormControl(this.panel.substrate.thickness, Validators.required),
      substrate: new FormControl(this.panel.substrate, Validators.required)
    });
  }

  sizeChange(size: Size) {
    if (size != null) {
      this.selectedSize = size;

      if (this.selectedThickness != null) {
        this.getSubstrates();
      }
    } else {
      this.selectedSize = null;
    }
  }

  thicknessChange(thickness: Thickness) {
    if (thickness != null) {
      this.selectedThickness = thickness;

      if (this.selectedSize != null) {
        this.getSubstrates();
      }
    } else {
      this.selectedThickness = null;
    }
  }

  getSubstrates() {
    this.substrateService.filter(this.selectedSize._id, this.selectedThickness._id)
      .toPromise().then(response => {
        this.substrates = response;
      })
      .catch();
  }


  get substrate() {
    return this.panelForm.get('substrate').value as Substrate;
  }

}

服务

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Thickness } from '../classes/thickness';
import { Size } from '../classes/size';
import { Substrate } from '../classes/substrate';
import { OrderItem } from '../classes/order-item';

@Injectable({
  providedIn: 'root'
})
export class ThicknessService {
    thicknesses: Thickness[] = JSON.parse( `[{ "_id": 1, "value": 0.75 }, 
                                   { "_id": 2, "value": 1 }, 
                                   { "_id": 3, "value": 1.25 }]`);

    public getList(): Observable<Thickness[]> {
      return of(this.thicknesses);
    }

}

@Injectable({
  providedIn: 'root'
})
export class SizeService {
    sizes: Size[] = JSON.parse( `[{ "_id": 1, "height": 8, "width": 4 }, 
                                   { "_id": 2, "height": 10, "width": 5 }, 
                                   { "_id": 3, "height": 12, "width": 5 }]`);

    public getList(): Observable<Size[]> {
      return of(this.sizes);
    }

}

@Injectable({
  providedIn: 'root'
})
export class SubstrateService {
    substrates: Substrate[] = JSON.parse( `[{
        "_id": 1,
        "name": "MDF",
        "size": { "_id": 1, "height": 8, "width": 4 },
        "thickness": { "_id": 1, "value": 0.75 }
    },
    {
        "_id": 2,
        "name": "PB",
        "size": { "_id": 2, "height": 10, "width": 5 },
        "thickness": { "_id": 2, "value": 1 }
    },
    {
        "_id": 3,
        "name": "WB",
        "size": { "_id": 3, "height": 12, "width": 5 },
        "thickness": { "_id": 3, "value": 1.25 }
    }
]`);

    public filter(sizeId: number, thicknessId: number): Observable<Substrate[]> {
      return of(this.substrates.filter(s => s.size._id === sizeId && s.thickness._id === thicknessId));
    }
}

@Injectable({
  providedIn: 'root'
})
export class OrderItemService {
    orderItem: OrderItem = JSON.parse( `{ "substrate": {
        "_id": 2,
        "name": "PB",
        "size": { "_id": 2, "height": 10, "width": 5 },
        "thickness": { "_id": 2, "value": 1 }
    } }`);

    newItem = new OrderItem();

    public getSingle(id: number): Observable<OrderItem> {
      if (id === 1) return of(this.orderItem);
      return of(this.newItem);
    }
}

1 个答案:

答案 0 :(得分:0)

SubstrateSizeThickness是类。这些类别的实例与的实例不同。 Substrate中的OrderItem。因此,当您像现在这样绑定[ngValue]并在模型中设置表单值时,它们将不匹配。

我建议对代码进行以下更改以使其正常运行。该示例仅适用于Size,但可以应用于所有三种类型:

<option *ngFor="let s of sizes" [ngValue]="s._id">{{ s.width }} x {{ s.height }}</option>
this.panelForm = new FormGroup({
  size: new FormControl(this.panel.substrate.size._id, Validators.required),
});

当然,您以后需要时将_id转换回一个对象(通过在sizes数组中查找它)。