Angular 2 Reactive Forms запускает подтверждение для отправки
Есть ли способ, чтобы все валидаторы реактивных форм могли срабатывать при подаче, а не только "грязными" и "касательными" событиями?
Причина этого заключается в том, что мы имеем очень большие формы, которые не указывают, требуется ли поле или нет, и пользователь может пропустить часть требуемого элемента управления, поэтому после отправки ожидается, что все недопустимые поля которые будут пропущены конечным пользователем.
Я пробовал маркировать форму как "тронутую", используя
FormGroup.markAsTouched(true);
это сработало, и поэтому я также попытался отметить его как "грязный"
FormGroup.markAsDirty(true);
но css класса по-прежнему "ng-pristine",
Есть ли способ запустить его вручную из компонента, я пробовал использовать его без изменений, спасибо заранее!
UPDATE
Я уже работал, итерации в FormGroup.controls и отмечая его как "грязный", но есть ли "стандартный" способ сделать это.
Ответы
Ответ 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});