首页 文章

Angular 2 Reactive Forms会在提交时触发验证

提问于
浏览
26

是否有一种方法可以在提交时触发所有被动形式的验证器,而不仅仅是“脏”和“触摸”事件?

这样做的原因是我们有一个非常大的表单,它不表示是否需要字段,用户可能会错过一些必需的控件,因此在提交时,预计会丢失所有错误的字段最终用户将被显示 .

我已经尝试通过使用标记将表单标记为“触摸”

FormGroup.markAsTouched(true);

它工作,所以我也尝试将其标记为“脏”

FormGroup.markAsDirty(true);

但是 class 的css仍然是“原始的”,

有没有办法从组件手动触发它,我试着谷歌搜索无济于事,谢谢你提前!

UPDATE

我已经通过迭代FormGroup.controls并将其标记为“脏”来完成它,但是有一种“标准”方法来执行此操作 .

8 回答

  • 9

    这可以通过 here 示例实现,您可以使用 NgForm 指令:

    <form [formGroup]="heroForm" #formDir="ngForm">
    

    然后在验证消息中检查表单是否已提交:

    <small *ngIf="heroForm.hasError('required', 'formCtrlName') && formDir.submitted">
      Required!
    </small>
    
  • 1

    有多种方法可以解决问题 . 如果你有嵌套的表单组,@ Splaktar的答案将不起作用 . 所以,这是适用于嵌套表单组的解决方案 .

    解决方案1:遍历所有表单组和表单控件,并以编程方式触摸它们以触发验证 .

    Template code:

    <form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
    ...
    <button type="submit" class="btn btn-success">Save</button>
    </form>
    

    component.ts code:

    onSubmit() {
            if (this.myForm.valid) {
                // save data
            } else {
                this.validateAllFields(this.myForm); 
            }
        }
    
    validateAllFields(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.validateAllFields(control);  
                }
            });
        }
    

    解决方案2:使用变量检查表单是否已提交 . 仅供参考:ngForm的提交字段目前正在测试中,将包含在未来的Angular版本中 . 因此不需要创建自己的变量 .

    component.ts code

    private formSubmitAttempt: boolean;
    
    onSubmit() {
            this.formSubmitAttempt = true;
            if (this.myForm.valid) {
                console.log('form submitted');
            }
       }
    

    Template code:

    <form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
        <div class="form-group">
            <label class="center-block">
                Name:
                <input class="form-control" formControlName="name">
            </label>
            <div class="alert alert-danger" *ngIf="myForm.get('name').hasError('required') && formSubmitAttempt">
                Name is required
            </div>
            ...
    </form>
    
  • 2

    几个月后回来,我在这里分享基于所有评论的改进版本,仅供记录:

    markAsTouched(group: FormGroup | FormArray) {
      group.markAsTouched({ onlySelf: true });
    
      Object.keys(group.controls).map((field) => {
        const control = group.get(field);
        if (control instanceof FormControl) {
          control.markAsTouched({ onlySelf: true });
        } else if (control instanceof FormGroup) {
          this.markAsTouched(control);
        }
      });
    }
    

    希望它会有用!

  • 13

    这可以通过markAsTouched()完成 . 在PR #26812合并之前,您可以使用

    function markAllAsTouched(group: AbstractControl) {
      group.markAsTouched({onlySelf: true});
      group._forEachChild((control: AbstractControl) => markAllAsTouched(control));
    }
    

    您可以在source code找到更多信息 .

  • 3

    我找到了一些可能感兴趣的东西:

    在提交时,我设置了 submitAttempt = true 并将其放在应该进行验证的div中:

    nickname.touched || nickname.dirty || (nickname.untouched && submitAttempt)

    含义:如果没有被触及,我们尝试提交,则错误显示 .

  • 0

    我的应用程序有很多表单和输入,所以我创建了各种自定义表单组件(用于普通文本输入,textarea输入,选择,复选框等),这样我就不需要重复详细的HTML / CSS和表单验证UI逻辑在这个地方 .

    我的自定义基本表单组件查找其主机 FormGroupDirective 并使用其 submitted 属性以及 FormControl 状态( validtouched 等)来确定哪些验证状态和消息(如果有)需要在UI上显示 .

    这个解决方案

    • 不需要遍历表单的控件并修改其状态

    • 不需要为每个控件添加一些额外的 submitted 属性

    • 不需要在 ngSubmit -binded onSubmit 方法中进行任何其他表单验证处理

    • 没有将模板驱动的表单与反应形式相结合

    form-base.component:

    import {Host, Input, OnInit, SkipSelf} from '@angular/core';
    import {FormControl, FormGroupDirective} from '@angular/forms';
    
    
    export abstract class FormBaseComponent implements OnInit {
    
      @Input() id: string;
      @Input() label: string;
      formControl: FormControl;
    
      constructor(@Host() @SkipSelf()
                  private formControlHost: FormGroupDirective) {
      }
    
      ngOnInit() {
        const form = this.formControlHost.form;
        this.formControl = <FormControl>form.controls[this.id];
        if (!this.formControl) {
          throw new Error('FormControl \'' + this.id + '\' needs to be defined');
        }
      }
    
      get errorMessage(): string {
        // TODO return error message based on 'this.formControl.errors'
        return null;
      }
    
      get showInputValid(): boolean {
        return this.formControl.valid && (this.formControl.touched || this.formControlHost.submitted);
      }
    
      get showInputInvalid(): boolean {
        return this.formControl.invalid && (this.formControl.touched || this.formControlHost.submitted);
      }
    }
    

    form-text.component:

    import {Component} from '@angular/core';
    import {FormBaseComponent} from '../form-base.component';
    
    @Component({
      selector: 'yourappprefix-form-text',
      templateUrl: './form-text.component.html'
    })
    export class FormTextComponent extends FormBaseComponent {
    
    }
    

    form-text.component.html:

    <label class="x_label" for="{{id}}">{{label}}</label>
    <div class="x_input-container"
         [class.x_input--valid]="showInputValid"
         [class.x_input--invalid]="showInputInvalid">
      <input class="x_input" id="{{id}}" type="text" [formControl]="formControl">
      <span class="x_input--error-message" *ngIf="errorMessage">{{errorMessage}}</span>
    </div>
    

    Usage:

    <form [formGroup]="form" novalidate>
      <yourappprefix-form-text id="someField" label="Some Field"></yourappprefix-form-text>
    </form>
    
  • 0

    如果我得到你要求的东西 . 您只想更新每次提交的验证消息 . 执行此操作的最佳方法是存储控件状态的历史记录 .

    export interface IValdiationField {
      submittedCount: number;
      valid: boolean;
    }
    class Component {
       // validation state management
       validationState: Map<string, IValdiationField | number> = new Map();
    
       constructor() {
          this.validationState.set('submitCount', 0);
       }
    
       validationChecker(formControlName: string): boolean {
    
        // get submitted count
        const submittedCount: number = (this.validationState.get('submitCount') || 0) as number;
    
        // form shouldn't show validation if form not submitted
        if (submittedCount === 0) {
           return true;
        }
    
        // get the validation state
        const state: IValdiationField = this.validationState.get(formControlName) as IValdiationField;
    
        // set state if undefined or state submitted count doesn't match submitted count
        if (state === undefined || state.submittedCount !== submittedCount) {
           this.validationState.set(formControlName, { submittedCount, valid: this.form.get(formControlName).valid } );
           return this.form.get(formControlName).valid;
         }
    
            // get validation value from validation state managment
           return state.valid;
       }
       submit() {
         this.validationState.set('submitCount', (this.validationState.get('submitCount') as number) + 1);
       } 
    }
    

    然后在html代码* ngIf =“!validationChecker('formControlName')”中显示错误消息 .

  • 7

    “脏”,“触摸”,“提交”可以使用下一个方法组合:

    <form [formGroup]="form" (ngSubmit)="doSomething()" #ngForm="ngForm">
    <input type="text" placeholder="Put some text" formControlName="textField" required>
    <div *ngIf="textField.invalid && (textField.dirty || textField.touched || ngForm.submitted)">
      <div *ngIf="textField.errors.required">Required!</div>
    </div>
    <input type="submit" value="Submit" />
    </form>
    

相关问题