Angular Material Select - Default Value - Reactive Forms

非 Y 不嫁゛ 提交于 2021-01-29 10:17:07

问题


I'm losing it here. There are so many questions on this subject, and no matter what I try I can't get it to work.

I'm simply wanting to set the default value of a mat-select.

I have a list of Employees. Clicking on a list item opens a dialog (Employee Details) so I can edit (or add) the Employee's data. I just want the Department mat-select to display the Employee's current department when the mode is edit.

employee-details.component.html

<div fxLayout="column">
   <div class="flex-item" fxFlex mat-dialog-actions fxLayoutAlign="end">
      <button mat-mini-fab color="" aria-label="Cancel" (click)="onCancelClick()">
         <mat-icon>clear</mat-icon>
      </button>
   </div>
   <h4 *ngIf="data.action == 'add'">Add New Employee</h4>
   <h4 *ngIf="data.action == 'edit'">Edit Employee</h4>
   <form [formGroup]="form" class="employee-form">
      <div class="form-group">
         <mat-form-field class="employee-full-width">
            <input
               formControlName="firstname"
               matInput
               required
               placeholder="Employee Firstname"
               >
            <mat-error
               *ngIf="
               form.get('firstname').touched && form.get('firstname').invalid
               "
               >Employee Firstname is Required!</mat-error
               >
         </mat-form-field>
         <mat-form-field class="employee-full-width">
            <input
               formControlName="lastname"
               matInput
               required
               placeholder="Employee Lastname"
               >
            <mat-error
               *ngIf="
               form.get('lastname').touched && form.get('lastname').invalid
               "
               >Employee Lastname is Required!</mat-error
               >
         </mat-form-field>
         <mat-form-field class="employee-full-width">
            <input
               formControlName="title"
               matInput
               required
               placeholder="Employee Title"
               >
            <mat-error
               *ngIf="
               form.get('title').touched && form.get('title').invalid
               "
               >Employee Title is Required!</mat-error
               >
         </mat-form-field>
         <mat-form-field class="employee-full-width">
            <input
               formControlName="rate"
               matInput
               required
               placeholder="Employee Rate"
               >
            <mat-error
               *ngIf="
               form.get('rate').touched && form.get('rate').invalid
               "
               >Employee Rate is Required!</mat-error
               >
         </mat-form-field>
         <mat-form-field class="employee-full-width">
            <mat-label>Department</mat-label>
            <mat-select formControlName="department">
               <mat-option *ngFor="let dept of departments" [value]="dept">
               {{ dept?.title }}
               </mat-option>
            </mat-select>
         </mat-form-field>
      </div>
   </form>
   <div class="flex-item" fxFlex mat-dialog-actions fxLayoutAlign="end">
      <button
         mat-raised-button
         color="secondary"
         class="project-add-button"
         (click)="onNoClick()"
         >
      Cancel
      </button>
      <button
      mat-raised-button
      [disabled]="!form.valid"
      color="primary"
      class="project-add-button"
      (click)="createEmployee(form)"
      >
      Save & Close
      </button>
      <button
      mat-raised-button
      [disabled]="!form.valid"
      color="primary"
      class="project-add-button"
      (click)="createEmployee(form)"
      >
      Save & New
      </button>
   </div>
</div>
<div>
   <div>
      <br />
      <br />
      <mat-accordion>
         <mat-expansion-panel>
            <mat-expansion-panel-header>
               <mat-panel-title>
                  Form value
               </mat-panel-title>
            </mat-expansion-panel-header>
            <code>
               <div fxLayout="column" fxLayoutGap="32px">
                  <div>{{ form.value | json }}</div>
                  <div>Form Validity: {{ form.valid | json}}</div>
               </div>
            </code>
         </mat-expansion-panel>
      </mat-accordion>
   </div>
</div>

employee-details.component.ts

import { Component, OnInit, Optional, Inject, AfterViewInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { EmployeeService } from 'src/app/core/services/entity/employee.service';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { DialogData } from 'src/app/employees/employee-list/employee-list.component';
import { DepartmentService } from 'src/app/core/services/entity/department.service';
import { Department } from 'src/app/shared/models/department';

@Component({
  selector: 'app-employee-details',
  templateUrl: './employee-details.component.html',
  styleUrls: ['./employee-details.component.css']
})
export class EmployeeDetailsComponent implements OnInit, AfterViewInit {
  departments: Department[];
  selectedDepartment: Department;

  form = new FormGroup({
    id: new FormControl(''),
    firstname: new FormControl(''),
    lastname: new FormControl(''),
    title: new FormControl(''),
    rate: new FormControl(''),
    department: new FormControl(''),
  });

  constructor(
    private departmentService: DepartmentService,
    private service: EmployeeService,
    public dialogRef: MatDialogRef<EmployeeDetailsComponent>,
    @Optional() @Inject(MAT_DIALOG_DATA) public data: DialogData
  ) {
    if (data.action === 'edit') {
      this.form.patchValue(data.employee);
    }
  }

  ngOnInit() {
    this.departmentService.departments$.subscribe(r => {
      this.departments = r;
    });
  }
  // **** THIS DOESN'T ACTUALLY DO ANYTHING, AS FAR AS I CAN TELL ****
  ngAfterViewInit() {
    this.form.controls.department.setValue(this.data.employee.department);
  }

  createEmployee(f: FormGroup) {
    if (this.data.action === 'edit') {
      this.service.updateEmployee(f.value).then(response => {
        this.closeDialog();
      });
      // creating a new one
    } else {
      this.service.createEmployee(f.value).then(response => {
        this.closeDialog();
      });
    }
  }

  onCancelClick() {
    this.onNoClick();
  }

  onNoClick() {
    this.dialogRef.close({
      event: 'cancel'
    });
  }

  closeDialog() {
    this.dialogRef.close({
      event: this.data.action,
      data: this.data.employee
    });
  }
}

I'm displaying the 'data' at the bottom of the dialog using:

<div>{{ form.value | json }}</div>

and it looks like (which tells me the Employee object is making it over to the dialog just fine):

{
    "id": "9UAPoKCFofH7aWBUwdiO",
    "firstname": "John",
    "lastname": "Doe",
    "title": "Manager",
    "rate": "120",
    "department": {
        "code": "1000",
        "id": "2PtDUwPBXT7pUPWTOibD",
        "title": "Project Management"
    }
}

To populate the departments mat-select, I'm using a service which CRUDs Firebase:

  ngOnInit() {
    this.departmentService.departments$.subscribe(r => {
      this.departments = r;
    });
  }

The relevant HTML from the template:

<mat-form-field class="employee-full-width">
  <mat-label>Department</mat-label>
    <mat-select formControlName="department">
      <mat-option *ngFor="let dept of departments" [value]="dept">
       {{ dept?.title }}
      </mat-option>
    </mat-select>
</mat-form-field>

employee.ts

import { Department } from './department';

export interface Employee {
    id: string;
    firstname: string;
    lastname: string;
    title: string;
    department: Department;
    rate: number;
}

department.ts

export interface Department {
    id: string;
    title: string;
    code: string;
}

回答1:


The reason is because the department object in your employee record does not match the department object in your query result. Yes, the values match, but the object reference does not match (its a separate object).

You can use the [compareWith] attribute to pass in a function that compares the ids of the objects. mat-select supports the same [compareWith] functionality as the standard Angular select. https://angular.io/api/forms/SelectControlValueAccessor#customizing-option-selection

So you would have this in your template:

<mat-select formControlName="department" [compareWith]="compareFn">

And then in your component:

compareFn(d1: Department, d2: Department): boolean {
    return d1 && d2 ? d1.id === d2.id : d1 === d2;
}



回答2:


Try

ngAfterViewInit() {
 this.form.controls['department'].setValue(this.data.employee.department);
}


来源:https://stackoverflow.com/questions/60050439/angular-material-select-default-value-reactive-forms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!