如何在 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 解释了解决方案。
这是循环遍历 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" })
}
}
我有如下验证摘要组件:
以下组件获取 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 解释了解决方案。
这是循环遍历 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" })
}
}