[英]Angular. Reactive form two way data binding
After data loaded i call reInit form.加载数据后,我调用 reInit 表单。 After manipulate with data from html template class didn't updated and at this moment i use this:
在使用来自 html 模板 class 的数据进行操作后没有更新,此时我使用这个:
Object.assign(this.customer, this.customerForm.getRawValue());
But I know that this is bad solution, and each every key and update data by conditions isn't good way.但我知道这是不好的解决方案,每个键和按条件更新数据都不是好方法。 Maby some easy way exists?
也许存在一些简单的方法?
I have some class, for Example:我有一些 class,例如:
export class Customer {
id: number|null = null;
short_name: string|null = null;
full_name: string|null = null;
contacts: Contact[] = [];
constructor(data?: Foo) { /* ... */ }
}
export class Contact {
id: number|null = null;
name: string|null = null;
general: boolean;
emails: {email: string|null, general: boolean}[];
phones: {phone: string|null, general: boolean}[];
}
for create all data empty, for existed Customer some filds filled.为创建所有数据为空,为现有客户填充一些字段。
now create a form:现在创建一个表单:
// convenience getters for easy access to form fields
get controls(): {[key: string]: AbstractControl} { return this.customerForm.controls; }
get contacts(): FormArray { return this.controls.contacts as FormArray; }
emails(contact: any): FormArray { return contact.controls.emails as FormArray; }
phones(contact: any): FormArray { return contact.controls.phones as FormArray; }
private initForm(customer?: Customer) {
this.customerForm = this.formBuilder.group({
short_name: [customer?.short_name, Validators.required],
full_name: [customer?.full_name, Validators.required],
user_id: [customer?.user_id, Validators.required],
site: [customer?.site],
contacts: new FormArray([])
});
this.customer.contacts.map(c => this.addContact(c));
}
private addContact(c: Contact): FormGroup {
const contactFormGroup = this.formBuilder.group({
id: [c?.id],
name: [c?.name, [
Validators.required,
CustomerContactValidator.createContact,
CustomerContactValidator.onlyOneGeneralEmail,
CustomerContactValidator.onlyOneGeneralPhone,
]],
general: [c?.general],
position: [c?.position],
comment: [c?.comment],
emails: new FormArray([]),
phones: new FormArray([])
});
this.contacts.push(contactFormGroup);
c.emails.map(ed => this.addContactEmail(contactFormGroup, ed));
c.phones.map(pd => this.addContactPhone(contactFormGroup, pd));
return contactFormGroup;
}
private addContactEmail(contactFormGroup: FormGroup, ed: ContactEmail, contactFormGroupIndex: number|null = null): void {
const emails = contactFormGroup.controls.emails as FormArray;
if (emails) {
const contactEmailFormGroup = this.formBuilder.group({
type: [ed?.type],
general: [ed?.general],
email: [ed?.email, [Validators.required, Validators.email]]
});
emails.push(contactEmailFormGroup);
if (contactFormGroupIndex !== null) {
setTimeout(() => {
document.getElementById(`email-${contactFormGroupIndex}-${emails.length-1}`)?.focus();
}, 50);
}
this.afterManipulateWithContactContacts(contactFormGroup);
}
}
private addContactPhone(contactFormGroup: FormGroup, pd: ContactPhone, contactFormGroupIndex: number|null = null): void {
const phones = contactFormGroup.controls.phones as FormArray;
if (phones) {
const contactPhoneFormGroup = this.formBuilder.group({
type: [pd?.type],
general: [pd?.general],
phone: [pd?.phone, [Validators.required, Validators.pattern(PHONE_PATTERN)]]
});
phones.push(contactPhoneFormGroup);
if (contactFormGroupIndex !== null) {
setTimeout(() => {
document.getElementById(`phone-${contactFormGroupIndex}-${phones.length-1}`)?.focus();
}, 50);
}
this.afterManipulateWithContactContacts(contactFormGroup);
}
}
and html:和 html:
<form class="customer-form" [formGroup]="customerForm">
<ng-container *ngFor="let contact of contacts.controls; let i = index" >
<div [formGroup]="$any(contact)" fxLayout="column" fxLayout.gt-md="row">
<div fxFlex="100" fxFlex.gt-md="20" fxFlex.gt-lg="15" [ngClass.gt-md]="'pr-2'">
<mat-checkbox
[ngClass.gt-md]="'mr-2'"
[ngClass.lt-lg]="'mr-1'"
formControlName="general"
(change)="changeGeneralContact($any(contact))"
></mat-checkbox>
<mat-form-field floatLabel="never" style="width: calc(100% - 24px)">
<input formControlName="name" matInput placeholder="ФИО" type="text">
<mat-error *ngIf="contact.get('name')?.hasError('required')">Заполните <strong>ФИО</strong></mat-error>
<mat-error *ngIf="contact.get('name')?.hasError('createContact')">Добавьте <strong>email</strong> или <strong>телефон</strong></mat-error>
<mat-error *ngIf="contact.get('name')?.hasError('onlyOneGeneralEmail')">Только <strong>один email</strong> может быть основным</mat-error>
<mat-error *ngIf="contact.get('name')?.hasError('onlyOneGeneralPhone')">Только <strong>один телефон</strong> может быть основным</mat-error>
</mat-form-field>
</div>
<div fxFlex="100" fxFlex.gt-md="25" fxFlex.gt-lg="20" [ngClass.gt-md]="'px-2'">
<div *ngFor="let email of emails($any(contact)).controls; let ei = index">
<div [formGroup]="$any(email)" class="d-flex align-items-center">
<mat-checkbox
[ngClass.gt-md]="'mr-2'"
[ngClass.lt-lg]="'mr-1'"
formControlName="general"
(change)="changeContactGeneralSubContact($any(contact), $any(email), contactSubType.email)"
></mat-checkbox>
<crm-email-edit [formGroup]="$any(email)" floatLabel="never" style="width: calc(100% - 60px)"></crm-email-edit>
<button
mat-icon-button
matTooltip="Удалить email"
aria-label="Удалить email"
(click)="removeContactEmailContact(i, ei, $any(contact))"
>
<mat-icon color="warn">delete</mat-icon>
</button>
</div>
</div>
<div>
<button
mat-icon-button
color="primary"
[matTooltip]="'Добавить email'"
(click)="createContactEmailContact($any(contact), i)"
>
<mat-icon>add_circle</mat-icon>
</button>
</div>
</div>
<div fxFlex="100" fxFlex.gt-md="25" fxFlex.gt-lg="20" [ngClass.gt-md]="'px-2'">
<div *ngFor="let phone of phones($any(contact)).controls; let pi = index">
<div [formGroup]="$any(phone)">
<mat-checkbox
[ngClass.gt-md]="'mr-2'"
[ngClass.lt-lg]="'mr-1'"
formControlName="general"
(change)="changeContactGeneralSubContact($any(contact), $any(phone), contactSubType.phone)"
></mat-checkbox>
<mat-form-field floatLabel="never">
<input [id]="'phone-'+i+'-'+pi" formControlName="phone" matInput placeholder="Телефон" type="number">
<mat-error *ngIf="phone.get('phone')?.hasError('required')">Заполните <strong>Телефон</strong> или удалите</mat-error>
<mat-error *ngIf="phone.get('phone')?.hasError('pattern')"><strong>Телефон</strong> указан не верно</mat-error>
</mat-form-field>
<button
mat-icon-button
matTooltip="Удалить телефон"
aria-label="Удалить телефон"
(click)="removeContactPhoneContact(i, pi, $any(contact))"
>
<mat-icon color="warn">delete</mat-icon>
</button>
</div>
</div>
<div>
<button
mat-icon-button
color="primary"
[matTooltip]="'Добавить телефон'"
(click)="createContactPhoneContact($any(contact), i)"
>
<mat-icon>add_circle</mat-icon>
</button>
</div>
</div>
<div fxFlex="10" fxFlex.gt-md="5" [ngClass.gt-md]="'px-2'">
<button
mat-mini-fab
color="warn"
matTooltip="Удалить контакт"
aria-label="Удалить контакт"
(click)="removeContact(i)"
>
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</ng-container>
<div>
<button mat-fab (click)="createContact()" aria-label="Добавить контакт" color="primary">
<mat-icon>person_add</mat-icon>
</button>
</div>
</form>
There is no direct two-way model binding in reactive form, but in a sense has two way form binding.没有反应形式的直接双向 model 绑定,但在某种意义上具有双向形式绑定。
ABC: formControls(['My value'])
ABC: formControls(['My value'])
a.一个。 In HTML it will be updated by user.
在 HTML 中,它将由用户更新。
b.湾。 In component.ts you can update it by controllers only
ABC.PatchValue or setValue.
在 component.ts 中,您只能通过控制器更新它
ABC.PatchValue or setValue.
you can get all the value from getRawValue()
or .value
, but you cant manipulate these values to update the form directly.您可以从
getRawValue()
或.value
获取所有值,但您不能操纵这些值直接更新表单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.