问题描述
我正在重构前辈开发的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 (将#修改为@)