Подтвердить что ты не робот

Angular 2 Reactive Forms запускает подтверждение для отправки

Есть ли способ, чтобы все валидаторы реактивных форм могли срабатывать при подаче, а не только "грязными" и "касательными" событиями?

Причина этого заключается в том, что мы имеем очень большие формы, которые не указывают, требуется ли поле или нет, и пользователь может пропустить часть требуемого элемента управления, поэтому после отправки ожидается, что все недопустимые поля которые будут пропущены конечным пользователем.

Я пробовал маркировать форму как "тронутую", используя

FormGroup.markAsTouched(true);

это сработало, и поэтому я также попытался отметить его как "грязный"

FormGroup.markAsDirty(true);

но css класса по-прежнему "ng-pristine",

Есть ли способ запустить его вручную из компонента, я пробовал использовать его без изменений, спасибо заранее!

UPDATE

Я уже работал, итерации в FormGroup.controls и отмечая его как "грязный", но есть ли "стандартный" способ сделать это.

4b9b3361

Ответ 1

Это может быть достигнуто с представленным здесь образцом, где вы можете использовать директиву NgForm:

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

а затем в ваших сообщениях проверки просто проверьте, отправлена ли форма:

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

Ответ 2

Существует несколько способов решения проблемы. Ответ @Splaktar не будет работать, если у вас есть вложенные группы форм. Итак, вот решение, которое будет работать с вложенными группами форм.

Решение 1. Перебирайте все группы форм и формы и программно прикасайтесь к ним, чтобы инициировать проверки.

Код шаблона:

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

Код компонента:

    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. Используйте переменную, чтобы проверить, была ли форма отправлена ​​или нет. FYI: Представленное поле для ngForm в настоящее время тестируется и будет включено в будущие версии Angular. Поэтому нет необходимости создавать свою собственную переменную.

code.ts code

private formSubmitAttempt: boolean;

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

Код шаблона:

<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>

Ответ 3

Возвращаясь через несколько месяцев, я делюсь здесь улучшенной версией, основанной на всех комментариях, просто для записи:

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);
    }
  });
}

Надеюсь, это будет полезно!

ОБНОВЛЕНИЕ: Angular 8 представил FormGroup.markAllAsTouched() и он делает это!: D

Ответ 4

Это можно сделать с помощью markAsTouched(). Пока PR # 26812 не будет объединен, вы можете использовать

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

Вы можете узнать больше в исходном коде.

Ответ 5

Я нашел кое-что, что могло бы представлять интерес:

В submit я устанавливаю submitAttempt = true и помещаю его в div, где должна выполняться проверка:

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

значение: Если она не была затронута, и мы попытались отправить сообщение, появится ошибка.

Ответ 6

Мое приложение имеет множество форм и вкладок, поэтому я создал различные пользовательские компоненты формы (для нормальных текстовых входов, входов текстовых полей, выбора, флажков и т.д.), Так что мне не нужно повторять подробные HTML/CSS и полностью логически настраивать логику UI над местом.

Мой пользовательский компонент базовой формы ищет свой хостинг FormGroupDirective и использует его submitted свойство в дополнение к его состояниям FormControl (valid, touched и т.д.), Чтобы решить, какой статус проверки и сообщение (если есть) должны отображаться в пользовательском интерфейсе.

Это решение

  • не требует прохождения через элементы управления формой и изменения их статуса
  • не требует добавления некоторого дополнительного submitted свойства каждому элементу управления
  • не требует обработки дополнительной проверки формы в ngSubmit -binded onSubmit
  • не сочетает шаблонные формы с реактивными формами

Форма-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);
  }
}

Форма-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 {

}

Форма-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>

Использование:

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

Ответ 7

Если я получу то, о чем вы просите. Вы хотите обновлять сообщения проверки только для каждой отправки. Лучший способ сделать это - сохранить историю состояния управления.

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')", чтобы отобразить сообщение об ошибке.

Ответ 8

"грязный", "тронутый", "представленный" можно комбинировать, используя следующий метод:

<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>

Ответ 9

Теперь есть опция updateOn: submit, которая вызовет проверку при отправке, используйте как:

this.myForm = new FormGroup({},{updateOn: ‘submit});