角MatFormField不渲染

问题描述

我正在重构前辈开发的Angular组件。

以前,用户个人资料组件中弹出了一个编辑。 我正在将此功能提取到新组件中。

配置文件目录中有expert-profile component,我添加一个名为edit-profile component的新组件,该组件仅支持编辑弹出窗口。我使用了与expert-profile.component.html文件中相同的html实现。

问题在于所有mat标签都没有呈现。

这里是screenshot in which the edit popup was rendering correctly

这里是screenshot of the edit popup after refactoring

失败时,将引发以下错误ERROR Error: mat-form-field must contain a MatFormFieldControl.

在专家配置文件中实施了编辑弹出窗口时,未发生此错误

那么,问题出在哪里?

我的代码

edit-profile.component.html:

<!----------------------------------------------------------
                      POPUP EDIT PROFILE
----------------------------------------------------------->
<div class="editPopup" *ngIf="editMode">
  <mat-card>
    <div class="updateLoading" *ngIf="updatingProfile">
      <mat-spinner></mat-spinner>
    </div>

    <h1>
      {{
          this.translateService.getTranslation(
            this.translatePage,'profileEdition'
          )
        }}
    </h1>

    <!----------------------------------------
                      F  O  R  M
    ----------------------------------------->
    <form [formGroup]="editProfileForm" (ngSubmit)="updateExpert(editProfileForm.value)">
      <p>
        <!----------------------------------------
                      PERSONAL @R_671_4045@IONS
        ----------------------------------------->
        <!-- title -->
        <span>
          {{
              this.translateService.getTranslation(
                this.translatePage,'personalDatas'
              )
            }}
        </span>
        <mat-divider></mat-divider>

        <!-- content & input -->
        <!-- lastName -->
        <mat-form-field>
          <!-- <mat-label>
              {{
                this.translateService.getTranslation(
                  this.translatePage,'lastName'
                )
              }}
            </mat-label> -->
          <label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'lastName'
                )
              }}
          </label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'lastName'
                )
              }}" formControlName="lastName" value="{{ this.userInfos.lastName }}" />
        </mat-form-field>

        <!-- firstName -->
        <mat-form-field>
          <mat-label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'firstName'
                )
              }}
          </mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'firstName'
                )
              }}" formControlName="firstName" value="{{ this.userInfos.firstName }}" />
        </mat-form-field>

        <!-- language -->
        <mat-form-field>
          <mat-label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'languages'
                )
              }}
          </mat-label>
          <mat-select multiple [(ngModel)]="this.userInfos.languages" [ngModelOptions]="{ standalone: true }">
            <mat-option *ngFor="let language of this.allLanguages" value="{{ language }}">
              {{
                  this.translateService.getTranslation(
                    this.languagePage,language
                  )
                }}
            </mat-option>
          </mat-select>
        </mat-form-field>
        <br /><br />

        <!----------------------------------------
                      CONTACT @R_671_4045@IONS
        ----------------------------------------->
        <!-- title -->
        <span>
          {{
              this.translateService.getTranslation(
                this.translatePage,'contactInfos'
              )
            }}
        </span>
        <mat-divider></mat-divider>
        <mat-form-field>
          <mat-label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'email'
                )
              }}
          </mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'email'
                )
              }}" type="email" formControlName="email" value="{{ this.userInfos.email }}" />
        </mat-form-field>
        <mat-form-field>
          <mat-label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'phone'
                )
              }}
          </mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'phone'
                )
              }}" type="tel" formControlName="phone" value="{{ this.userInfos.phone }}" />
        </mat-form-field>
        <br /><br />

        <!----------------------------------------
                      COMPANY
        ----------------------------------------->
        <!-- title -->
        <span>
          {{
              this.translateService.getTranslation(
                this.translatePage,'company'
              )
            }}
        </span>
        <mat-divider></mat-divider>
        <mat-form-field>
          <mat-label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'company'
                )
              }}
          </mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'company'
                )
              }}" formControlName="company" value="{{ this.userInfos.company }}" />
        </mat-form-field>
        <mat-form-field>
          <mat-label>
            {{
                this.translateService.getTranslation(
                  this.translatePage,'title'
                )
              }}
          </mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'title'
                )
              }}" formControlName="title" value="{{ this.expert.title }}" />
        </mat-form-field>
        <mat-form-field appearance="legacy">
          <mat-label>{{
              this.translateService.getTranslation(
                this.translatePage,'expertWage'
              )
            }}</mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'expertWage'
                )
              }}" type="number" formControlName="wage" value="{{ this.expert.wage }}" />
        </mat-form-field>

  <!-- <mat-form-field style="width: 90%;">

           <mat-chip-list #chipList aria-label="Software selection">
            <mat-chip *ngFor="let software of this.currentSoftwares" [selectable]="true" [removable]="true"
              (removed)="remove(software)" disabled="{{ this.updatingProfile }}">
              {{ software }}
              <mat-icon matChipRemove>cancel</mat-icon>
            </mat-chip>
            <input placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'softwaresSelection'
                )
              }}" #softInput [formControl]="this.softwareCtrl" [matAutocomplete]="auto" [matChipInputFor]="chipList"
              [matChipInputSeparatorKeyCodes]="separatorKeysCodes" (matChipInputTokenEnd)="add($event)" />
          </mat-chip-list>
          <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
            <mat-option *ngFor="let software of this.filteredSoftwares | async" [value]="software">
              {{ software }}
            </mat-option>
          </mat-autocomplete>
        </mat-form-field> -->

        <br /><br />


        <!----------------------------------------
                      LOCATION
        ----------------------------------------->
        <!-- title -->
        <span>{{
            this.translateService.getTranslation(this.translatePage,'location')
          }}</span>
        <mat-divider></mat-divider>
        <mat-form-field appearance="legacy">
          <mat-label>{{
              this.translateService.getTranslation(
                this.translatePage,'citizenship'
              )
            }}</mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'citizenship'
                )
              }}" formControlName="citizenship" value="{{ this.userInfos.citizenship }}" />
        </mat-form-field>
        <mat-form-field appearance="legacy">
          <mat-label>{{
              this.translateService.getTranslation(
                this.translatePage,'country'
              )
            }}</mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'country'
                )
              }}" formControlName="country" value="{{ this.userInfos.country }}" />
        </mat-form-field>
        <mat-form-field appearance="legacy">
          <mat-label>{{
              this.translateService.getTranslation(this.translatePage,'city')
            }}</mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(this.translatePage,'city')
              }}" formControlName="city" value="{{ this.userInfos.city }}" />
        </mat-form-field>
        <br />
        <mat-form-field appearance="legacy">
          <mat-label>{{
              this.translateService.getTranslation(this.translatePage,'postal')
            }}</mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'postal'
                )
              }}" formControlName="address" value="{{ this.userInfos.address }}" />
        </mat-form-field>
        <mat-form-field appearance="legacy">
          <mat-label>{{
              this.translateService.getTranslation(
                this.translatePage,'zipCode'
              )
            }}</mat-label>
          <input matInput placeholder="{{
                this.translateService.getTranslation(
                  this.translatePage,'zipCode'
                )
              }}" type="number" formControlName="zipCode" value="{{ this.userInfos.zipCode }}" />
        </mat-form-field>
        <br /><br />

        <!----------------------------------------
                      COMPETENCIES
        ----------------------------------------->
        <!-- title -->
        <span>{{
            this.translateService.getTranslation(
              this.translatePage,'competencies'
            )
          }}</span>
        <mat-divider></mat-divider>

        <mat-card class="competencies">
          <mat-checkBox color="primary" class="competenciesCheck" formControlName="aerospace">
            Aerospace
          </mat-checkBox>

          <mat-checkBox color="primary" class="competenciesCheck" formControlName="energy">
            Energy
          </mat-checkBox>

          <mat-checkBox color="primary" class="competenciesCheck" formControlName="transport">
            Transport
          </mat-checkBox>

          <mat-checkBox color="primary" class="competenciesCheck" formControlName="industries">
            Industries
          </mat-checkBox>
        </mat-card>
      </p>
      <div class="submitButtons">
        <button mat-button (click)="CloseDialog()" #cancelEditButton>
          {{
              this.translateService.getTranslation(this.translatePage,'cancel')
            }}
        </button>
        <button mat-button color="primary" type="submit" #applyEditButton>
          {{
              this.translateService.getTranslation(this.translatePage,'modify')
            }}
        </button>
      </div>
    </form>
  </mat-card>
</div>

edit-profile.component.ts:

// import from libraries angular & RxJS
import {
  Component,ElementRef,Input,OnInit,Output,ViewChild,EventEmitter,ViewEncapsulation,ɵConsole,Inject,} from '@angular/core';
import { COMMA,ENTER } from '@angular/cdk/keycodes';
import { Observable } from 'rxjs';
import { map,startWith } from 'rxjs/operators';
import { Router } from '@angular/router';
import { FormBuilder,FormControl,FormGroup } from '@angular/forms';

// import from material
import {
  MatAutocomplete,MatAutocompleteSelectedEvent,} from '@angular/material/autocomplete';
import {
  MatDialog,MatDialogRef,MAT_DIALOG_DATA,} from '@angular/material/dialog';
import { MatButton } from '@angular/material/button';
import { MatChipInputEvent } from '@angular/material/chips';
import { MatSnackBar } from '@angular/material/snack-bar';
import {MatFormFieldModule} from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';

// import from app
import { Expert } from 'src/app/interfaces/expert/expert';
import {
  ComponentsTrslt,TranslationService,} from 'src/app/services/translation/translation.service';
import { ExpertService } from 'src/app/services/expert/expert.service';
import { UserService } from 'src/app/services/user/user.service';
import { ProjectService } from 'src/app/services/project/project.service';
import { User@R_671_4045@ion } from 'src/app/interfaces/user-@R_671_4045@ion/user-@R_671_4045@ion';
import { Project } from 'src/app/interfaces/project/project';
import { LanguageService } from 'src/app/services/language/language.service';
import { SoftwareService } from 'src/app/sub-modules/profile/services/software/software.service';
import { ExpertProfileComponent } from 'src/app/sub-modules/profile/shared/expert-profile/expert-profile.component';
import { Globals } from 'src/app/globals/globals';
import { UserProject } from 'src/app/interfaces/user-projects/user-project';
import { environment } from 'src/environments/environment';
import { AreaEnum } from 'src/app/sub-modules/profile/interfaces/area-enum/area-enum.enum';
import { FileManipulationsService } from 'src/app/services/file-operations/file-manipulations.service';
import { FavoritesService } from 'src/app/sub-modules/profile/services/favorites/favorites.service';
import { ExpertiseService } from 'src/app/sub-modules/profile/services/expertise/expertise.service';
import { ExpertisesEnums } from 'src/app/sub-modules/profile/interfaces/expertises/expertises.enum';

@Component({
  selector: 'app-edit-profile',templateUrl: './edit-profile.component.html',styleUrls: ['./edit-profile.component.less'],})
export class EditProfileComponent implements OnInit {
  // app translation management
  public translatePage: ComponentsTrslt = ComponentsTrslt.PROFILE;
  public languagePage: ComponentsTrslt = ComponentsTrslt.LANGUAGE;

  // pop up buttons
  private cancelInput: MatButton;
  @ViewChild('cancelEditButton',{ static: false }) set cancelContent(
    cancelContent: MatButton
  ) {
    if (cancelContent) {
      this.cancelInput = cancelContent;
    }
  }

  private applyInput: MatButton;
  @ViewChild('applyEditButton',{ static: false }) set applyContent(
    applyContent: MatButton
  ) {
    if (applyContent) {
      this.applyInput = applyContent;
    }
  }

  private softInput: ElementRef<HTMLInputElement>;
  @ViewChild('softInput',{ static: false }) set content(
    content: ElementRef<HTMLInputElement>
  ) {
    if (content) {
      // initially setter gets called with undefined
      this.softInput = content;
    }
  }
  // user info
  userInfos: User@R_671_4045@ion;
  // @ViewChild('auto',{ static: false }) matAutocomplete: MatAutocomplete;

  // edit pop up
  @input() profileId: string;

  public ownProfile: boolean = null; // Modify it to display edit/addFav button
  public editMode = false; // Used to display edit popup
  public updatingProfile = false; // Used to display spinner when updating profile after edit
  public connectedisExpert: boolean = null; // Modify it to display edit/addFav button
  editProfileForm: FormGroup;
  expert: Expert;
  public allAreas = []; // Array to display all areas in profile Edition
  public allLanguages = []; // Array to display all languages in profile Edition

  separatorKeysCodes: number[] = [ENTER,COMMA];
  public displayedSoft: string[] = [];
  public allSoftwares: string[] = [];
  softwareCtrl = new FormControl();
  filteredSoftwares: Observable<string[]>;

  // Used to show 'about' @R_671_4045@ions more simply
  // Need to load data in async
  public expertDatas = [
    { label: 'company',data: '-' },{ label: 'phone',{ label: 'email',{ label: 'location',{ label: 'citizenship',{ label: 'softwares',data: [] },{ label: 'languages',];

  // 'Hardcoded' enum,need to fill the "data" field with db datas
  public expertises = ExpertisesEnums;

  public currentSoftwares: string[] = [];

  constructor(
    public dialogRef: MatDialogRef<EditProfileComponent>,public translateService: TranslationService,private userService: UserService,private expertService: ExpertService,public expertiseService: ExpertiseService,public languageService: LanguageService,private softwareService: SoftwareService,private formBuilder: FormBuilder
  ) {
    this.editProfileForm = this.formBuilder.group({
      lastName: [{ value: '',disabled: true }],firstName: '',email: '',phone: '',company: '',title: '',wage: '',citizenship: '',country: '',city: '',address: '',zipCode: '',softwares: [],aerospace: false,energy: false,transport: false,industries: false,});
  }

  ngOnInit(): void {
    this.opClEditMode();
    // this.editMode = true;
    this.profileId = localStorage.getItem(Globals.USER_ID);
    this.ownProfile = true;
    // get data about user connected
    // Replace by request to get only if user is expert
    this.userService
      .getPartial(localStorage.getItem(Globals.USER_ID))
      .subscribe((user) => {
        console.log('#1 user: ( this.userService.getPartial )',user);
        this.connectedisExpert = user.expertId != null;
        console.log(
          '#1 connectedisExpert: ( this.userService.getPartial )',this.connectedisExpert
        );
      });

    this.getProfileInfo();

    // test
    console.log('   #ONINIT  -   editmode: ',this.editMode);
    // console.log('this.ownProfile: ',this.ownProfile);
    console.log('   #ONINIT  -   this.profileId: ',this.profileId);
    // console.log('localStorage.getItem(Globals.USER_ID) :',localStorage.getItem(Globals.USER_ID));
    console.log('   #ONINIT  -   this.connectedisExpert: ',this.connectedisExpert);
    console.log('   #ONINIT  -   this.translatePage: ',this.translatePage);
    console.log('   #ONINIT  -   this.languagePage: ',this.languagePage);
  }

  // update data about expert connected
  public updateExpert(formData) {
    const areasArray = [];

    if (formData.aerospace !== false) {
      areasArray.push('AEROSPACE');
    }
    if (formData.energy !== false) {
      areasArray.push('ENERGY');
    }
    if (formData.transport !== false) {
      areasArray.push('TRANSPORT');
    }
    if (formData.industries !== false) {
      areasArray.push('INDUSTRIES');
    }
    const str = formData.phone;
    const matches = str.replace(/\D/g,'');

    const newUserInfo: User@R_671_4045@ion = {
      lastName: formData.lastName,firstName: formData.firstName,email: formData.email,phone: matches,company: formData.company,citizenship: formData.citizenship,country: formData.country,city: formData.city,address: formData.address,zipCode: formData.zipCode,image: this.userInfos.image,softwares: formData.softwares,languages: this.userInfos.languages,areas: areasArray,};

    this.updatingProfile = true;
    this.applyInput.disabled = true;
    this.cancelInput.disabled = true;

    this.userService.updateUser(this.profileId,newUserInfo).subscribe((_) => {
      const newExpertInfo: Expert = {
        title: formData.title,wage: formData.wage,rating: null,};

      this.expertService
        .updateUser(this.expert.id,newExpertInfo)
        .subscribe((expert) => {
          this.opClEditMode();

          this.updatingProfile = false;
          this.applyInput.disabled = false;
          this.cancelInput.disabled = false;

          setTimeout(() => {
            this.getProfileInfo();
          },2);
        });
    });
  }

  // open - close dialog
  CloseDialog(): void {
    this.dialogRef.close();
  }

  opClEditMode() {
    this.editMode = !this.editMode;
    console.log('this.editMode: (opClEditMode()) ',this.editMode); // test
    if (this.editMode) {
      this.getProfileInfo();
    }
  }

  // to fill input with data allready registered in db
  getProfileInfo() {
    console.log('this.profileId: ( getProfileInfo() )',this.profileId);
    this.userService
      .getPartial(this.profileId)
      .subscribe((user: User@R_671_4045@ion) => {
        this.userInfos = user;
        console.log('this.userInfos: (  getProfileInfo() ) ',this.userInfos); // test
        this.expertDatas[0].data = user.company;
        this.expertDatas[1].data = user.phone;
        this.expertDatas[2].data = user.email;
        this.expertDatas[3].data = user.address;
        this.expertDatas[4].data = user.citizenship;

        this.editProfileForm.setValue({
          lastName: this.userInfos.lastName,firstName: this.userInfos.firstName,email: this.userInfos.email,phone: this.userInfos.phone,company: this.userInfos.company,citizenship: this.userInfos.citizenship,country: this.userInfos.country,city: this.userInfos.city,address: this.userInfos.address,zipCode: this.userInfos.zipCode,softwares: '',});

        // Get Expert Info
        this.expertService
          .getByUserId(this.profileId)
          .subscribe((expert: Expert) => {
            this.expert = expert;

            this.expertises = this.expertiseService.getProfileExpertises(
              this.expert.id
            );

            // Get User Languages info
            this.languageService
              .getLanguagesByUserId(this.profileId)
              .subscribe((e) => {
                this.userInfos.languages = e.map((elem) => elem.name);
                this.expertDatas[6].data = this.userInfos.languages;

                // Get USer Softwares Info
                this.softwareService
                  .getSoftwaresByUserId(this.profileId)
                  .subscribe((k) => {
                    this.userInfos.softwares = k.map((elem) => elem.name);
                    this.expertDatas[5].data = this.userInfos.softwares;
                    this.currentSoftwares = this.userInfos.softwares;
                    // this.noSoftDuplicate();

                    // Get User "Areas" info.
                    this.expertService
                      .getAreasNames(this.profileId)
                      .subscribe((userAreas) => {
                        this.userInfos.areas = userAreas;

                        this.editProfileForm.patchValue({
                          title: this.expert.title,wage: this.expert.wage,softwares: this.userInfos.softwares,});

                        // Here we prefill the form with user info,there are obvIoUsly better ways to do this,but the with the
                        // current implementation of user "Areas" this is easier for Now. We would have to manually add areas here
                        // (if we increase the diversity of areas in the DB) and at several other places in this file.
                        if (userAreas.includes('AEROSPACE')) {
                          this.editProfileForm.patchValue({
                            aerospace: true,});
                        }
                        if (userAreas.includes('ENERGY')) {
                          this.editProfileForm.patchValue({
                            energy: true,});
                        }
                        if (userAreas.includes('TRANSPORT')) {
                          this.editProfileForm.patchValue({
                            transport: true,});
                        }
                        if (userAreas.includes('INDUSTRIES')) {
                          this.editProfileForm.patchValue({
                            industries: true,});
                        }
                      });
                  });
              });
          });
      });
  }

  add(event: MatChipInputEvent): void {
    const input = event.input;
    const value = event.value;

    // Add our software
    if ((value || '').trim()) {
      this.currentSoftwares.push(value.trim());
    }

    // Reset the input value
    if (input) {
      input.value = '';
    }
    this.softwareCtrl.setValue(null);

    this.noSoftDuplicate();
  }

  selected(event: MatAutocompleteSelectedEvent): void {
    this.currentSoftwares.push(event.option.viewValue);
    this.softInput.nativeElement.value = '';
    this.softwareCtrl.setValue(null);
    this.noSoftDuplicate();
  }

  remove(software: string): void {
    const index = this.currentSoftwares.indexOf(software);
    if (index >= 0) {
      this.currentSoftwares.splice(index,1);
    }
    this.noSoftDuplicate();
  }

  public noSoftDuplicate() {
    this.displayedSoft = [];
    this.displayedSoft = this.allSoftwares.map((software) => {
      if (
        this.currentSoftwares.find((e: string) => e === software) === undefined
      ) {
        return software;
      }
    });
  }
}

profile.module.ts

包含:

import { MatInputModule } from '@angular/material/input';
import { FormsModule,ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field';
[...]
imports: [
    CommonModule,ProfileRoutingModule,MatProgressspinnerModule,MatSlidetoggleModule,MatToolbarModule,MatIconModule,MatButtonModule,MatInputModule,MatCardModule,MatProgressBarModule,ReactiveFormsModule,MatDividerModule,MatSelectModule,FormsModule,MatAutocompleteModule,MatChipsModule,ImgFallbackModule,MatCheckBoxModule,MatTooltipModule,MatFormFieldModule,],})
export class ProfileModule {}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)