角子组件和验证消息

时间:2018-10-04 10:25:32

标签: angular

我有以下带有表单的子组件,并且当父组件提交主表单时,需要在我的控制下显示错误消息。

import {
  Component, ChangeDetectorRef, forwardRef,
  NgModule, OnInit
} from '@angular/core';
import {
  FormGroup, FormControl, FormBuilder, Validators,
  NG_VALIDATORS, Validator, AbstractControl, ValidationErrors
} from '@angular/forms';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

export const ADDRESS_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => AddressFormComponent),
  multi: true
};

export const ADDRESS_VALIDATORS: any = {
  provide: NG_VALIDATORS,
  useExisting: forwardRef(() => AddressFormComponent),
  multi: true
};

@Component({
  selector: 'app-address-form',
  templateUrl: 'address-form.component.html',
  providers: [ADDRESS_VALUE_ACCESSOR, ADDRESS_VALIDATORS],
})
export class AddressFormComponent implements OnInit, ControlValueAccessor, Validator {

  addressForm: FormGroup;

  private innerValue: any;

  onModelTouched: Function = () => { };
  onModelChange: Function = () => { };

  constructor(
    private _fb: FormBuilder,
    private ref: ChangeDetectorRef,

  ) { }

  ngOnInit() {

    this.addressForm = this._fb.group({
      'via': new FormControl('', Validators.required),
      'civico': new FormControl(''),
      'cap': new FormControl(''),
      'comune': new FormControl(''),
      'provincia': new FormControl(''),
      'regione': new FormControl(''),
      'stato': new FormControl(''),
      'frazione': new FormControl('')
    });

  }

  // get accessor
  get value(): any {
    return this.innerValue;
  }

  // set accessor including call the onchange callback
  set value(v: any) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onModelChange();
    }
  }

  // Set touched on blur
  onBlur() {
    this.onModelTouched();
  }

  writeValue(value: any): void {
    this.value = value;

    if (this.value != null) {
      this.addressForm.get('via').setValue(this.value.via);
      this.addressForm.get('civico').setValue(this.value.civico);
      this.addressForm.get('cap').setValue(this.value.cap);
      this.addressForm.get('comune').setValue(this.value.comune);
      this.addressForm.get('provincia').setValue(this.value.provincia);
      this.addressForm.get('frazione').setValue(this.value.frazione);
      this.addressForm.get('stato').setValue(this.value.stato);
      this.addressForm.get('regione').setValue(this.value.regione);
    }

    this.ref.markForCheck();
  }

  registerOnChange(fn: Function): void {

    this.addressForm.valueChanges.subscribe(() => {
      fn(this.addressForm.value);
    });

    this.onModelChange = fn;
  }

  registerOnTouched(fn: Function): void {
    this.onModelTouched = fn;
  }

  validate(c: AbstractControl): ValidationErrors | null {
    return this.addressForm.valid ? null : { subformerror: 'Problems in subform!' };
  }

  registerOnValidatorChange(fn: () => void): void {
    this.addressForm.statusChanges.subscribe(fn);
  }
}

@NgModule({
  imports: [

    CommonModule,
    FormsModule,
    ReactiveFormsModule

  ],
  exports: [AddressFormComponent],
  declarations: [
    AddressFormComponent
  ],
  entryComponents: [AddressFormComponent],
  providers: [

  ]
})

export class AddressFormModule { }

查看:

<form [formGroup]="addressForm">

  <div class="row">
      <div class="form-group col-md-10">
          <label for="txtVia">Via</label>
          <input type="text" pInputText class="form-control" id="txtVia"
              formControlName="via">
              <div *ngIf="!addressForm.controls['via'].valid && (addressForm.controls['via'].dirty || addressForm.controls['via'].touched)"
              class="alert alert-danger">
              Campo obbligatorio
          </div>
      </div>
      <div class="form-group col-md-2">
          <label for="txtCivico">Civico</label>
          <input type="text" pInputText class="form-control" id="txtCivico"
              formControlName="civico">
      </div>
  </div>
  <div class="row">
      <div class="form-group col-md-3">
          <label for="txtCap">Cap</label>
          <input type="text" pInputText class="form-control" id="txtCap"
              formControlName="cap">
      </div>
      <div class="form-group col-md-6">
          <label for="txtComune">Comune</label>
          <input type="text" pInputText class="form-control" id="txtComune"
              formControlName="comune">
      </div>
      <div class="form-group col-md-3">
          <label for="txtProvincia">Provincia</label>
          <input type="text" pInputText class="form-control" id="txtProvincia"
              formControlName="provincia">
      </div>
  </div>
  <div class="form-group">
      <label for="txtFrazione">Frazione</label>
      <input type="text" pInputText class="form-control" id="txtFrazione"
          formControlName="frazione">
  </div>
  <div class="row">
      <div class="form-group col-md-6">
          <label for="txtRegione">Regione</label>
          <input type="text" pInputText class="form-control" id="txtRegione"
              formControlName="regione">
      </div>
      <div class="form-group col-md-6">
          <label for="txtStato">Stato</label>
          <input type="text" pInputText class="form-control" id="txtStato"
              formControlName="stato">
      </div>
  </div>

</form>

我通过以下方式将其绑定到父表单:

视图:

<form (submit)="onConfirm($event)" *ngIf="aziendaform" [formGroup]="aziendaform">
    <app-address-form formControlName="indirizzoSpedizione"></app-address-form>
</form>

代码:

import { Component, Input, Output, EventEmitter, OnInit, SimpleChanges, OnChanges, ChangeDetectorRef } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';

@Component({
    selector: 'app-azienda-form',
    templateUrl: 'aziende-form.component.html'
})
export class AziendeFormComponent implements OnInit, OnChanges {

    aziendaform: FormGroup;


    constructor(
        private fb: FormBuilder,
        private cdr: ChangeDetectorRef
    ) {
    }

    ngOnChanges(changes: SimpleChanges) {

    }

    ngOnInit(): void {

        this.aziendaform = this.fb.group({          
            'indirizzoSpedizione': new FormControl(null)         
        });

        this.cdr.detectChanges();
    }

    validateAllFormFields(formGroup: FormGroup) {
        Object.keys(formGroup.controls).forEach(field => {
            const control = formGroup.get(field);
            if (control instanceof FormControl) {
                control.markAsTouched({ onlySelf: true });
            } else if (control instanceof FormGroup) {
                this.validateAllFormFields(control);
            }
        });
    }

    onConfirm(event) {
        event.preventDefault();

        if (this.aziendaform.valid) {
            /*
                do somithing
            */
        } else {
            this.validateAllFormFields(this.aziendaform);
        }
    }

}

当我按下确认按钮时,该表单无效,但是在子组件中没有错误消息。 似乎markAsTouched不会传播为子表单。

有人可以帮我吗?

更新

我在这里准备了StackBlitz: https://stackblitz.com/edit/angular-4qcsox

如您所见,确认并重置按钮不会影响子组件。

1 个答案:

答案 0 :(得分:2)

如果您单步执行validateAllFormFields(),将会看到indirizzoSpedizione保持为空,子控件将永远不会被连线。

我通过在父组件中建立一个空表单组来使其工作:

   this.aziendaform = this.fb.group({
        'indirizzoSpedizione':new FormGroup({})
    });

然后让子组件接受一个FormGroup作为输入

export class AddressFormComponent implements OnInit, ControlValueAccessor, Validator {

@Input() addressForm: FormGroup;

并通过addControl添加控件

ngOnInit() {

    this.addressForm.addControl('via', new FormControl('', Validators.required));

父组件然后传递空的formGroup

<app-address-form  [addressForm]="aziendaform.controls['indirizzoSpedizione']"></app-address-form>

这是您提出的stackBlitz解决方案 https://stackblitz.com/edit/angular-stm9kw