Angular - 动态输入表单数组更新失败
Angular - Dynamic input form array fails to update
我正在 Reactive Form 中实现动态输入字段 FormArray 以在 Angular-12 中执行更新。我有这个代码:
接口:
export interface IResponse<T> {
message: string;
error: boolean;
code: number;
results: T;
}
export interface IEmployees {
employees: IEmployee[];
}
export class EmployeeResponse {
results!: { employee: IEmployee; };
}
export interface IEmployee {
id?: number;
current_residential_address?: string;
employeephones?: IContact[];
}
export interface IContact {
id?: number;
phone_number: string;
phone_type_id?: number;
phonetypes?: {id:number,type_name:string};
is_primary_contact_number?: boolean;
}
服务:
getContactById(id: number): Observable<EmployeeResponse> {
return this.http.get<EmployeeResponse>(this.api.baseURL + 'company/employees/fetchbyid/' + id, this.httpOptions);
}
public updateContact(id: number, employee: IEmployee): Observable<any> {
return this.http.post(this.api.baseURL + 'employees/contact/update/' + id, employee, this.httpOptions);
}
组件:
isLoading = false;
isSubmitted = false;
contactdata!: IEmployee;
contactInfoForm!: FormGroup;
ngOnInit(): void {
this.isLoading = true;
this._id = this.route.snapshot.params['id'];
this.updateContact();
this.loadContactById();
}
loadContactById() {
this.employeeService
.getContactById(this._id)
.subscribe((data: EmployeeResponse) => {
this.contactdata = data.results.employee;
this.contactInfoForm.patchValue({
current_residential_address: this.contactdata.current_residential_address,
});
this.contactInfoForm.setControl(
'contacts',
this.SetExistingContacts(this.contactdata.employeephones || [])
);
});
}
SetExistingContacts(contactSets: IContact[]): FormArray {
const formarray = new FormArray([]);
contactSets.forEach(c => {
formarray.push(this.fb.group({
phone_number: c.phone_number,
phone_type_id: c.phone_type_id,
is_primary_contact_number: c.is_primary_contact_number
}));
});
return formarray;
}
updateContact() {
this.contactInfoForm = this.fb.group({
id: [''],
current_residential_address: ['', [Validators.required]],
contacts: this.fb.array([
this.addContactFormGroup()
])
});
}
addContactFormGroup(): FormGroup {
return this.fb.group({
phone_type_id: ['', Validators.required],
phone_number: ['', [Validators.required, Validators.maxLength(15)]],
is_primary_contact_number: ['']
});
}
get fc() {
return this.contactInfoForm.controls;
};
public addContactButtonClick() {
const contacts = this.contactInfoForm.get('contacts') as FormArray
contacts.push(this.addContactFormGroup())
}
get contacts() {
return this.contactInfoForm.controls['contacts'] as FormArray;
}
getContactsFormArray(): FormArray {
return this.contactInfoForm.get('contacts') as FormArray;
}
get contactArray(): FormArray {
return <FormArray > this.contactInfoForm.get('contacts');
}
onSubmitContact() {
this.isSubmitted = true;
if (this.contactInfoForm.invalid) {
return;
}
this.isLoading = true;
this.mapFormValueForContactModel();
this.employeeService.updateContact(this._id, this.contactdata).subscribe(res => {
this.data = res;
});
}
mapFormValueForContactModel() {
this.contactdata.current_residential_address = this.contactInfoForm.value.current_residential_address;
this.contactdata.employeephones = this.contactInfoForm.value.employeephones;
}
<form [formGroup]="contactInfoForm" (ngSubmit)="onSubmitContact()">
<div class="row">
<div class="col-12 col-md-12">
<div class="form-group">
<label for="current_residential_address">Current Residential Address:<span style="color:red;">*</span></label>
<textarea rows="2" formControlName="current_residential_address" name="description" type="text" placeholder="22, Alexander Close ..." class="form-control mb-3" required>
</textarea>
</div>
<div *ngIf="fc.current_residential_address.touched && fc.current_residential_address.invalid">
<div *ngIf="fc.current_residential_address.hasError('required')">
<div class="text-danger">
Current Residential Address is required!
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div formArrayName="contacts" class="col-md-12">
<div *ngFor="let contact of getContactsFormArray().controls; let i = index" [formGroupName]="i">
<p>
<b>Contact Phone : {{i + 1}}</b>
</p>
<hr>
<div class="row">
<div class="col-12 col-md-4">
<div class="form-group">
<label for="phone_number">Phone Number:<span style="color:red;">*</span></label>
<div class="input-group mb-4">
<ngx-intl-tel-input [cssClass]="'form-control mb-4'" [preferredCountries]="preferredCountries" [enableAutoCountrySelect]="false" [enablePlaceholder]="true" [searchCountryFlag]="true" [searchCountryField]="[SearchCountryField.Iso2, SearchCountryField.Name]"
[selectFirstCountry]="false" [selectedCountryISO]="CountryISO.Scotland" [phoneValidation]="true" [separateDialCode]="true" name="phone_number" formControlName="phone_number">
</ngx-intl-tel-input>
</div>
</div>
<div *ngIf="getContactFormGroup(i).get('phone_number')!.touched && getContactFormGroup(i).get('phone_number')!.invalid">
<div *ngIf="getContactFormGroup(i).get('phone_number')!.hasError('required')">
<div class="text-danger">
Phone Number is required!
</div>
</div>
<div *ngIf="getContactFormGroup(i).get('phone_number')!.hasError('validatePhoneNumber')">
<div class="text-danger">
Invalid Phone Number!
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="form-group">
<label for="phone_type_id">Phone Type:<span style="color:red;">*</span></label>
<ng-select [items]="phonetypes" [selectOnTab]="true" [searchable]="true" bindValue="id" bindLabel="type_name" placeholder="Select Phone Type" [multiple]="false" [clearable]="true" required formControlName="phone_type_id">
</ng-select>
</div>
</div>
<div class="col-12 col-md-2">
<div class="form-group">
<br><button type="button" class="btn btn-danger float-right" (click)="removeOrClearContact(i)"><i class="fas fa-times-circle"></i> Remove</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer">
<button type="button" class="btn btn-primary float-right" (click)="addContactButtonClick()"><i class="fas fa-plus-circle"></i> Add</button>
<button type="submit" class="btn btn-success" [disabled]="isLoading" class="btn btn-success" (click)="contactValidate()">
<span *ngIf="isLoading" class="spinner-border spinner-border-sm mr-1"></span>
<i class="fa fa-save" aria-hidden="true"></i> Save</button>
</div>
</form>
我有两组数据:单一数据(current_residential_address)和动态表格数组。
当我提交更新数据时,只更新了current_residential_address,但数组(联系人)没有更新。
我该如何纠正?
谢谢
在你的FormGroup
中,没有employeephones
FormArray,而是contacts
FormArray。
updateContact() {
this.contactInfoForm = this.fb.group({
id: [''],
current_residential_address: ['', [Validators.required]],
contacts: this.fb.array([this.addContactFormGroup()]),
});
}
mapFormValueForContactModel() {
this.contactdata.current_residential_address = this.contactInfoForm.value.current_residential_address;
this.contactdata.employeephones = this.contactInfoForm.value.employeephones;
}
解决方案
将this.contactInfoForm.value.employeephones
替换为this.contactInfoForm.value.contacts
.
并使用 phone_number
将 this.contactInfoForm.value.contacts
映射到 return 所需的输出,因为 FormGroup
中的 phone_number
是 return 对象(包含phone 数字的多种类型)由 ngx-intl-tel-input
.
mapFormValueForContactModel() {
this.contactdata.current_residential_address =
this.contactInfoForm.value.current_residential_address;
this.contactdata.employeephones =
this.contactInfoForm.value.contacts.map(
(value) => {
return {
phone_type_id: value.phone_type_id,
is_primary_contact_number: value.is_primary_contact_number,
phone_number: value.phone_number.e164Number
};
}
);
}
我正在 Reactive Form 中实现动态输入字段 FormArray 以在 Angular-12 中执行更新。我有这个代码:
接口:
export interface IResponse<T> {
message: string;
error: boolean;
code: number;
results: T;
}
export interface IEmployees {
employees: IEmployee[];
}
export class EmployeeResponse {
results!: { employee: IEmployee; };
}
export interface IEmployee {
id?: number;
current_residential_address?: string;
employeephones?: IContact[];
}
export interface IContact {
id?: number;
phone_number: string;
phone_type_id?: number;
phonetypes?: {id:number,type_name:string};
is_primary_contact_number?: boolean;
}
服务:
getContactById(id: number): Observable<EmployeeResponse> {
return this.http.get<EmployeeResponse>(this.api.baseURL + 'company/employees/fetchbyid/' + id, this.httpOptions);
}
public updateContact(id: number, employee: IEmployee): Observable<any> {
return this.http.post(this.api.baseURL + 'employees/contact/update/' + id, employee, this.httpOptions);
}
组件:
isLoading = false;
isSubmitted = false;
contactdata!: IEmployee;
contactInfoForm!: FormGroup;
ngOnInit(): void {
this.isLoading = true;
this._id = this.route.snapshot.params['id'];
this.updateContact();
this.loadContactById();
}
loadContactById() {
this.employeeService
.getContactById(this._id)
.subscribe((data: EmployeeResponse) => {
this.contactdata = data.results.employee;
this.contactInfoForm.patchValue({
current_residential_address: this.contactdata.current_residential_address,
});
this.contactInfoForm.setControl(
'contacts',
this.SetExistingContacts(this.contactdata.employeephones || [])
);
});
}
SetExistingContacts(contactSets: IContact[]): FormArray {
const formarray = new FormArray([]);
contactSets.forEach(c => {
formarray.push(this.fb.group({
phone_number: c.phone_number,
phone_type_id: c.phone_type_id,
is_primary_contact_number: c.is_primary_contact_number
}));
});
return formarray;
}
updateContact() {
this.contactInfoForm = this.fb.group({
id: [''],
current_residential_address: ['', [Validators.required]],
contacts: this.fb.array([
this.addContactFormGroup()
])
});
}
addContactFormGroup(): FormGroup {
return this.fb.group({
phone_type_id: ['', Validators.required],
phone_number: ['', [Validators.required, Validators.maxLength(15)]],
is_primary_contact_number: ['']
});
}
get fc() {
return this.contactInfoForm.controls;
};
public addContactButtonClick() {
const contacts = this.contactInfoForm.get('contacts') as FormArray
contacts.push(this.addContactFormGroup())
}
get contacts() {
return this.contactInfoForm.controls['contacts'] as FormArray;
}
getContactsFormArray(): FormArray {
return this.contactInfoForm.get('contacts') as FormArray;
}
get contactArray(): FormArray {
return <FormArray > this.contactInfoForm.get('contacts');
}
onSubmitContact() {
this.isSubmitted = true;
if (this.contactInfoForm.invalid) {
return;
}
this.isLoading = true;
this.mapFormValueForContactModel();
this.employeeService.updateContact(this._id, this.contactdata).subscribe(res => {
this.data = res;
});
}
mapFormValueForContactModel() {
this.contactdata.current_residential_address = this.contactInfoForm.value.current_residential_address;
this.contactdata.employeephones = this.contactInfoForm.value.employeephones;
}
<form [formGroup]="contactInfoForm" (ngSubmit)="onSubmitContact()">
<div class="row">
<div class="col-12 col-md-12">
<div class="form-group">
<label for="current_residential_address">Current Residential Address:<span style="color:red;">*</span></label>
<textarea rows="2" formControlName="current_residential_address" name="description" type="text" placeholder="22, Alexander Close ..." class="form-control mb-3" required>
</textarea>
</div>
<div *ngIf="fc.current_residential_address.touched && fc.current_residential_address.invalid">
<div *ngIf="fc.current_residential_address.hasError('required')">
<div class="text-danger">
Current Residential Address is required!
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div formArrayName="contacts" class="col-md-12">
<div *ngFor="let contact of getContactsFormArray().controls; let i = index" [formGroupName]="i">
<p>
<b>Contact Phone : {{i + 1}}</b>
</p>
<hr>
<div class="row">
<div class="col-12 col-md-4">
<div class="form-group">
<label for="phone_number">Phone Number:<span style="color:red;">*</span></label>
<div class="input-group mb-4">
<ngx-intl-tel-input [cssClass]="'form-control mb-4'" [preferredCountries]="preferredCountries" [enableAutoCountrySelect]="false" [enablePlaceholder]="true" [searchCountryFlag]="true" [searchCountryField]="[SearchCountryField.Iso2, SearchCountryField.Name]"
[selectFirstCountry]="false" [selectedCountryISO]="CountryISO.Scotland" [phoneValidation]="true" [separateDialCode]="true" name="phone_number" formControlName="phone_number">
</ngx-intl-tel-input>
</div>
</div>
<div *ngIf="getContactFormGroup(i).get('phone_number')!.touched && getContactFormGroup(i).get('phone_number')!.invalid">
<div *ngIf="getContactFormGroup(i).get('phone_number')!.hasError('required')">
<div class="text-danger">
Phone Number is required!
</div>
</div>
<div *ngIf="getContactFormGroup(i).get('phone_number')!.hasError('validatePhoneNumber')">
<div class="text-danger">
Invalid Phone Number!
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="form-group">
<label for="phone_type_id">Phone Type:<span style="color:red;">*</span></label>
<ng-select [items]="phonetypes" [selectOnTab]="true" [searchable]="true" bindValue="id" bindLabel="type_name" placeholder="Select Phone Type" [multiple]="false" [clearable]="true" required formControlName="phone_type_id">
</ng-select>
</div>
</div>
<div class="col-12 col-md-2">
<div class="form-group">
<br><button type="button" class="btn btn-danger float-right" (click)="removeOrClearContact(i)"><i class="fas fa-times-circle"></i> Remove</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer">
<button type="button" class="btn btn-primary float-right" (click)="addContactButtonClick()"><i class="fas fa-plus-circle"></i> Add</button>
<button type="submit" class="btn btn-success" [disabled]="isLoading" class="btn btn-success" (click)="contactValidate()">
<span *ngIf="isLoading" class="spinner-border spinner-border-sm mr-1"></span>
<i class="fa fa-save" aria-hidden="true"></i> Save</button>
</div>
</form>
我有两组数据:单一数据(current_residential_address)和动态表格数组。
当我提交更新数据时,只更新了current_residential_address,但数组(联系人)没有更新。
我该如何纠正?
谢谢
在你的FormGroup
中,没有employeephones
FormArray,而是contacts
FormArray。
updateContact() {
this.contactInfoForm = this.fb.group({
id: [''],
current_residential_address: ['', [Validators.required]],
contacts: this.fb.array([this.addContactFormGroup()]),
});
}
mapFormValueForContactModel() {
this.contactdata.current_residential_address = this.contactInfoForm.value.current_residential_address;
this.contactdata.employeephones = this.contactInfoForm.value.employeephones;
}
解决方案
将this.contactInfoForm.value.employeephones
替换为this.contactInfoForm.value.contacts
.
并使用 phone_number
将 this.contactInfoForm.value.contacts
映射到 return 所需的输出,因为 FormGroup
中的 phone_number
是 return 对象(包含phone 数字的多种类型)由 ngx-intl-tel-input
.
mapFormValueForContactModel() {
this.contactdata.current_residential_address =
this.contactInfoForm.value.current_residential_address;
this.contactdata.employeephones =
this.contactInfoForm.value.contacts.map(
(value) => {
return {
phone_type_id: value.phone_type_id,
is_primary_contact_number: value.is_primary_contact_number,
phone_number: value.phone_number.e164Number
};
}
);
}