After data loaded i call reInit form. After manipulate with data from html template class didn't updated and at this moment i use this:
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:
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:
<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.
ABC: formControls(['My value'])
a. In HTML it will be updated by user.
b. In component.ts you can update it by controllers only 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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.