如何在 angular 响应式表单中的表单顶部显示错误消息摘要

How to display the summary of error message on top of the form in angular reactive forms

我有如下验证摘要组件:

以下组件获取 ngform 但无法订阅 statuschanges/Valuechanges 并显示错误消息摘要。

export class ValidationSummaryComponent implements OnInit {
  @Input() form: NgForm;
  errors: string[] = [];

 constructor() { }

ngOnInit() {
  if (this.form instanceof NgForm === false) {
  throw new Error('You must supply the validation summary with an NgForm.');
}
this.form.statusChanges.subscribe(status => {
  this.resetErrorMessages();
  this.generateErrorMessages(this.form.control);
});
}

 resetErrorMessages() {
this.errors.length = 0;
}

generateErrorMessages(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(controlName => {
  const control = formGroup.controls[controlName];
  console.log('control.......... ', control);
  const errors = control.errors;
  console.log('control.errors.......... ', control.errors);

  if (errors === null || errors.count === 0) {
    return;
  }
  // Handle the 'required' case
  if (errors.required) {
    this.errors.push(`${controlName} is required`);
  }
  // Handle 'minlength' case
  if (errors.minlength) {
    this.errors.push(`${controlName} minimum length is ${errors.minlength.requiredLength}.`);
  }
  // Handle custom messages.
  if (errors.message) {
    this.errors.push(`${controlName} ${errors.message}`);
  }
});

} }

和验证-summary.component.html

<div *ngIf="errors?.length > 0" class="validation-summary">
    <p>Please fix the following errors:</p>
<ul>
  <li *ngFor="let error of errors">{{ error }}</li>
</ul>

动态-form.component.ts

constructor(public formBuilder: FormBuilder) {
}
productFormGroup: FormArray;

ngOnInit() {

this.productFormGroup = this.formBuilder.array([]);
  this.ProductList.forEach(product => {
      this.productFormGroup.push(this.formBuilder.group({
        productId: [product.productId],
        displayName: [product.displayName],
        productName: [product.productName, Validators.required]
      }));
    });

} }

使用验证摘要的动态-form.component.html。

    <div id="mainWrapper">
      <app-validation-summary-component [form]="userForm"></app-validation-summary-component> 
    <form #userForm="ngForm">
<div [formGroup]="product" *ngFor="let product of 
         productFormGroup.controls>
      <label>{{product.get('displayName').value}}</label>
         <input  class="form-control" 
       name="product.get('displayName').value" 
        formControlName="productName" required/>
 </div>
      <button class="btn btn-primary" type="submit" 
              [disabled]="!productFormGroup.valid">Submit </button>
       <button class="btn btn-primary" style="margin-left: 30px" type="reset" 
       (click)="reset()"> Reset </button>
       </form>
</div>

需要在显示标签名称列表的动态表单组件顶部显示错误消息摘要,即 blank/Invalid。

您在验证中循环 formarray formgroup 控件的方式不正确-summary.component。创建的 stackblitz 解释了解决方案。

https://stackblitz.com/edit/angular-mr49zh

这是循环遍历 FormGroup 的每个 FromControl 的函数组,包括 FormArray 中的嵌套 FormControl 和生成验证摘要。

  getFormSummaryHtml(formGroup, fg_discription){
    let allErrors = this.getFormSummary(formGroup, '');
    let error_obj = this.setErrorFormat(allErrors, fg_discription)
    if (error_obj){
      let html = this.getHtmlFromObject(error_obj, '')   
  
      return html;
    }
  }

  getHtmlFromObject(error_obj, html) {
    if (error_obj.value) {
      for (let index = 0; index < error_obj.value.length; index++) {
        const element = error_obj.value[index];
        html += `<ul> <li>`;
        html += element.text;

        if (element.value) {
          html = this.getHtmlFromObject(element.value, html)
        }
        html += ` </li> </ul>`;
      }
    }
    else if (error_obj.length > 0) {
      html += `<ul>`;
      for (let index = 0; index < error_obj.length; index++) {
        const element = error_obj[index];
        html += element.text;
        if (element.value) {
          html = this.getHtmlFromObject(element.value, html)
        }
        else {
          // to Remove last appended text
          html = html.substring(0, html.length - element.text.length);
          html += `<li> ${element.text} </li>`
        }
      }
      html += `</ul>`;
    }
    else {
      html += error_obj.text;
    }
    return html
  }

  getFormSummary(formGroup, parentGroup) {
    let errors = []
    for (const key in formGroup) {
      if (Object.prototype.hasOwnProperty.call(formGroup, key)) {
        const element = formGroup[key];
        if (element instanceof FormGroup) {
          parentGroup = parentGroup == '' ? key : parentGroup + '>' + key;
          let terror = this.getFormSummary(element.controls, parentGroup)
          errors = [...errors, ...terror]
        }
        if (element instanceof FormArray) {
          parentGroup = parentGroup == '' ? key : parentGroup + '>' + key;
          for (let index = 0; index < element.controls.length; index++) {
            let ele = element.controls[index];
            let terror = this.getFormSummary(ele["controls"], parentGroup)
            errors = [...errors, ...terror]            
          }
        }
        if (element instanceof FormControl && element.valid == false && element.errors && Object.keys(element.errors).length > 0) {
          for (const errorTypes in element.errors) {
            errors.push(parentGroup + '>' + key + ':' + errorTypes)
          }
        }
      }
    }    
    return errors;
  }

  setErrorFormat(errors,discription){
    let errorObj: { text: string, key: string, value: Array<any>};

    for (let index = 0; index < errors.length; index++) {
      let element = errors[index];
      let elements = element.split(':');
      let error_elements = elements[0].split('>')
      error_elements.forEach((ele,idx) => {
        if(errorObj == null){
          errorObj = { text: discription[ele], key: ele, value: null };
        }
        else if (idx > 0){
          let error_t_obj = this.getErrorElement(errorObj, ele);

          let error_ele_obj = this.getErrorElement(errorObj, error_elements[idx - 1]);
          if (error_ele_obj && (error_t_obj == null || error_t_obj.key != error_elements[idx])) {
            let obj = { text: discription[ele], key: ele, value: null };
            error_ele_obj.value = error_ele_obj.value && error_ele_obj.value.length > 0 ? error_ele_obj.value : new Array();
            error_ele_obj.value.push(obj)
          }
        }        
      });      
    }
    return errorObj;
  }

  getErrorElement(error,s_key){
    if (error.key == s_key) {
      return error;
    }
    else if (error.value && error.value.length > 0) {
      for (let index = 0; index < error.value.length; index++) {
        let ele = error.value[index];
        let objerror = this.getErrorElement(ele, s_key);
        if (objerror != null ){
          return objerror;
        }
      }
    }
  }

这个怎么称呼?

getFormSummary(){
// This array contains FromControls name and Names of controls that user can understand
let fg_discription = {
  saleGroup: "Sales",
  sale_date: "Sale date",
  sale_type_id: "Sale type",
  deal_type_id: "Deal type",
  lender_id: "Lender",
  wholesaler_id: "Wholesaler",
  published_price: "Published price",
  pack: "Pack",
  allowance: "Allowance"
};
let html = this.getFormSummaryHtml(this.addSalesForm.controls, fg_discription)
return html;
}

validateFormOnSubmission() {
let form = this.addSalesForm.value;
let html = this.getFormSummary()
if (html == undefined) {
// Submit Form
    this.onSubmitAddSales();
}
else { // Show Html in POP message
  this.popMessage({ detail: "Following fields are  mandatory: ", data: { html }, key: "popup" })
}

}