简体   繁体   English

Angular。 反应式表单双向数据绑定

[英]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 绑定,但在某种意义上具有双向形式绑定。

  1. You provide value to ABC: formControls(['My value'])您为ABC: formControls(['My value'])
  2. ABC controller can be updated from controller or HTML template. ABC controller 可以从 controller 或 HTML 模板更新。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM