打开编辑时,Angular 无法以 react 形式填充垫选择控件

标签 angular typescript angular-reactive-forms

我是 Angular 的新手,我认为我在一个非常简单的步骤上浪费了很多时间。

我有一个非常简单的表单,用于在数据库中创建Question实体。创建记录时,表单似乎工作正常。但是,当它打开编辑现有记录时,“mat-select”控制字段不会被填充。这些下拉菜单也从 api 加载。

以下是代码。

组件.ts

import { Component, Inject, inject, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Observable } from 'rxjs';
import { Question } from 'src/app/Models/Question';
import { QuestionCategory } from 'src/app/Models/QuestionCategory';
import { QuestionType } from 'src/app/Models/QuestionType';
import { StResponse } from 'src/app/Models/StResponse';
import { QuestionCategoryService } from 'src/app/Services/question-category.service';
import { QuestionTypeService } from 'src/app/Services/question-type.service';
import { QuestionService } from 'src/app/Services/question.service';

@Component({
  selector: 'app-question-form',
  templateUrl: './question-form.component.html',
  styleUrls: ['./question-form.component.scss']
})
export class QuestionFormComponent implements OnInit {

  // model: Question;
  @ViewChild(FormGroupDirective)
  formDirective!: FormGroupDirective;
  questionForm!: FormGroup

  questionTypes: QuestionType[] = []
  questionCategories: QuestionCategory [] = [];

  constructor(private _fb: FormBuilder, 
              private _service: QuestionService,
              private _quesCategoryServ: QuestionCategoryService,
              private _quesTypeServ: QuestionTypeService,
              @Inject(MAT_DIALOG_DATA) private data: Question
              ) {
    
  }

  ngOnInit(): void {
    console.log('QF Oninit', this.data);
    this._quesCategoryServ.getAll().subscribe((sr:StResponse)=>{
      console.log('QesComp:', sr.data)
      this.questionCategories = sr.data.QuestionCategory;
    });
    this._quesTypeServ.getAll().subscribe((sr:StResponse)=>{
      console.log('QesComp:', sr.data)
      this.questionTypes = sr.data.QuestionType;
    });

    this.questionForm = this._fb.group({
      shortText: ['',Validators.required],
      text: ['',Validators.required],
      textInfo: [''],
      questionType: [null,Validators.required],
      questionCategory:[null,Validators.required],
    });

    if(this.data){
      console.log('oninit-forEdit')
      this.questionForm.patchValue({
        shortText: this.data.shortText,
        text: this.data.text,
        textInfo: this.data.textInfo,
        questionType: this.data.questionType,
        questionCategory: this.data.questionCategory,
      });

    }
  }

  onFormSubmit():void {
    if(this.questionForm.valid){
      console.log('submited form values',this.questionForm.value);
      this._service.add(this.questionForm.value).subscribe({
        next: (val: StResponse) => {
          alert('Question Added')
          // this.questionForm.reset();
          this.formDirective.resetForm();
          console.log('form resetted!')
        },
        error: (err: any) =>{
          console.error(err);
        }
      })
    }
  }
  
  // FormControl fields references for validation
  get shortText() { return this.questionForm.get('shortText'); }
  get text() { return this.questionForm.get('text'); }
  get textInfo() { return this.questionForm.get('textInfo'); }
  get questionType() { return this.questionForm.get('questionType'); }
  get questionCategory() { return this.questionForm.get('questionCategory'); }

}

组件.html

<h1 mat-dialog-title>Question</h1>
<form [formGroup]="questionForm" (ngSubmit)="onFormSubmit()">
    <div mat-dialog-content class="content">
        <div class="row">
            <mat-form-field appearance="outline">
                <mat-label>Short Text</mat-label>
                <input matInput type="text" placeholder="Ex. Wheels." formControlName="shortText">
                <mat-error *ngIf="shortText?.invalid">Enter short text.</mat-error>
            </mat-form-field>
        </div>
        <div class="row">
            <mat-form-field appearance="outline">
                <mat-label>Question</mat-label>
                <input matInput type="text" placeholder="Ex. Undamaged, All nuts tight." formControlName="text">
                <mat-error *ngIf="text?.invalid">Please enter question text.</mat-error>
            </mat-form-field>
        </div>
        <div class="row">
        <mat-form-field appearance="outline">
            <mat-label>Details</mat-label>
            <textarea matInput placeholder="Ex. Any details to further explain this question." formControlName="textInfo"></textarea>
        </mat-form-field>
        </div>

        <div class="row">
            <mat-form-field appearance="outline">
                <mat-label>Type</mat-label>
                <mat-select formControlName="questionType" >
                    <mat-option *ngFor="let typeOption of questionTypes" [value]="typeOption"> 
                    {{typeOption.code}} 
                    </mat-option>
                </mat-select>
                <mat-error *ngIf="questionType?.invalid">You must make a selection</mat-error>
            </mat-form-field>
        </div>
        <div class="row">
            <mat-form-field appearance="outline">
                <mat-label>Category</mat-label>
                <mat-select formControlName="questionCategory" >
                    <mat-option *ngFor="let categoryOption of questionCategories" [value]="categoryOption">
                    {{categoryOption.code}}
                    </mat-option>
                </mat-select>
                <mat-error *ngIf="questionCategory?.invalid">You must make a selection</mat-error>
            </mat-form-field>
        </div>
    </div>
    <div mat-dialog-actions class="action">
    <button mat-raised-button mat-dialog-close>Close</button>
    <button mat-raised-button color="primary" type="submit">save</button>
    </div>
</form>

export class Question{
    id: number;
    shortText: string;
    text: string;
    textInfo: string;
    questionType: QuestionType;
    questionCategory: QuestionCategory;

    constructor(){
        this.id = 0;
        this.shortText = '';
        this.text = '';
        this.textInfo = '';
        this.questionType = new QuestionType();
        this.questionCategory = new QuestionCategory();
    }
}

export class QuestionType{
    id: number;
    code: string;

    constructor(){
        this.id = 0;
        this.code = '';
    }
}

export class QuestionCategory{
    id: number;
    code: string;
    name: string;

    constructor(){
        this.id = 0;
        this.code = '';
        this.name = '';
    }
}

一些其他发现 正在填充 ShortText、text、TextInfo 的其他输入。只有 Mat-select 没有被加载。我一定错过了一些愚蠢的事情,因为即使选择的值也没有填充,它们就在那里,就好像我在打开编辑时将它们保留为空白,然后再次按“保存”。这些值正在提交并保存在数据库中。

我也尝试过 form.setValue() 方法,但仍然没有成功。

    // part of ngOnInit()

    if(this.data){
      console.log('oninit-forEdit')
      this.questionForm.patchValue({
        shortText: this.data.shortText,
        text: this.data.text,
        textInfo: this.data.textInfo,
        // questionType: this.data.questionType,
        // questionCategory: this.data.questionCategory,
      });

      const selectedCategory = this.questionCategories.find(category => category.id === this.data.questionCategory.id);
      const selectedType = this.questionTypes.find(type => type.id === this.data.questionType.id);


      this.questionForm.get('questionCategory')?.setValue(selectedCategory);
      this.questionForm.get('questionType')?.setValue(selectedType);
    }
  }

任何帮助或指导都会有帮助。谢谢。

最佳答案

在决定将哪个选项标记为选定时,Angular 会按标识 (===) 比较值。

这在编辑表单时不起作用,因为当前表单控件值不等于列表中的任何值,即。 它们不是相同的对象(即使它们具有相同的结构)。

questionTypes通过调用this._quesTypeServ.getAll获取,当前表单控件值为data.questionType

要使 Angular 以其他方式比较值,您可以使用 SelectControlValueAccessorcompareWith 输入。

我们可以通过 id 比较值,因为该属性对于每个对象都是唯一的:

compareFn(a: QuestionType, b: QuestionType): boolean {
    return a && b ? (a.id === b.id) : (a === b);
}

在模板中:

<mat-select formControlName="questionType" [compareWith]="compareFn">
    <mat-option *ngFor="let typeOption of questionTypes" [value]="typeOption"> 
        {{typeOption.code}} 
    </mat-option>
</mat-select>

有关compareWith的文档:https://angular.io/api/forms/SelectControlValueAccessor#customizing-option-selection

关于打开编辑时,Angular 无法以 react 形式填充垫选择控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76408758/

相关文章:

angular - 无法将数据映射库导入 Angular 2 应用程序的 HTML

node.js - Angular 8 - 无法让 httpClient POST 与 Socket.io 一起完成其工作?

typescript - 将带有 UMD 选项的 typescript 编译成单个文件

javascript - 有没有办法在泛型 (<T>) 函数/类 (Typescript) 中获取泛型类型的名称?

Angular 2 - 响应式(Reactive)表单验证消息

angular - 订阅'不可分配给类型

angular - 禁用 mdInput 文本字段的 float

typescript - Deno 编译时出现错误 : TS2339 [ERROR]: Property x does not exist . ..

angular - FormArray 长度不会被 form.reset() 重置

angular - 如何根据垫单选按钮的值禁用垫输入字段?