반응형 양식 - 필드를 터치됨으로 표시
모든 폼의 필드를 터치된 것으로 표시하는 방법을 찾는 데 어려움을 겪고 있습니다.가장 큰 문제는 필드를 터치하지 않고 폼을 제출하려고 하면 인증 오류가 나타나지 않는다는 것입니다.내 컨트롤러에 그 코드 조각의 자리 표시자가 있어
제 생각은 간단합니다.
- 사용자 클릭 전송 버튼
- 모든 필드가 터치된 것으로 표시됨
- error formatter가 다시 실행되어 검증 오류를 표시합니다.
새로운 방법을 도입하지 않고 제출 시 오류를 표시하는 방법을 알고 계신 분은 공유해 주십시오.감사합니다!
단순화된 폼:
<form class="form-horizontal" [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
<input type="text" id="title" class="form-control" formControlName="title">
<span class="help-block" *ngIf="formErrors.title">{{ formErrors.title }}</span>
<button>Submit</button>
</form>
컨트롤러는 다음과 같습니다.
import {Component, OnInit} from '@angular/core';
import {FormGroup, FormBuilder, Validators} from '@angular/forms';
@Component({
selector : 'pastebin-root',
templateUrl: './app.component.html',
styleUrls : ['./app.component.css']
})
export class AppComponent implements OnInit {
form: FormGroup;
formErrors = {
'title': ''
};
validationMessages = {
'title': {
'required': 'Title is required.'
}
};
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.buildForm();
}
onSubmit(form: any): void {
// somehow touch all elements so onValueChanged will generate correct error messages
this.onValueChanged();
if (this.form.valid) {
console.log(form);
}
}
buildForm(): void {
this.form = this.fb.group({
'title': ['', Validators.required]
});
this.form.valueChanges
.subscribe(data => this.onValueChanged(data));
}
onValueChanged(data?: any) {
if (!this.form) {
return;
}
const form = this.form;
for (const field in this.formErrors) {
if (!this.formErrors.hasOwnProperty(field)) {
continue;
}
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.touched && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
if (!control.errors.hasOwnProperty(key)) {
continue;
}
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
}
Angular 8부터는 간단하게 사용할 수 있습니다.
this.form.markAllAsTouched();
컨트롤과 그 하위 컨트롤에 터치 표시를 합니다.
다음 기능은 양식 그룹의 컨트롤을 반복하여 부드럽게 터치합니다.컨트롤의 필드가 객체이기 때문에 코드 호출은Object.values()
폼 그룹의 제어 필드에 있습니다.
/**
* Marks all controls in a form group as touched
* @param formGroup - The form group to touch
*/
private markFormGroupTouched(formGroup: FormGroup) {
(<any>Object).values(formGroup.controls).forEach(control => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
});
}
@masterwork의 답변에 대해서.이 솔루션을 시도했지만 함수가 FormGroup 내부를 재귀적으로 파려고 했을 때 오류가 발생했습니다.이것은 다음 행에 FormGroup이 아닌 FormControl 인수가 전달되어 있기 때문입니다.
control.controls.forEach(c => this.markFormGroupTouched(c));
이것이 나의 해결책이다.
markFormGroupTouched(formGroup: FormGroup) {
(<any>Object).values(formGroup.controls).forEach(control => {
if (control.controls) { // control is a FormGroup
markFormGroupTouched(control);
} else { // control is a FormControl
control.markAsTouched();
}
});
}
Angular v8에서는 이 기능을 내장하고 있습니다.markAllAsTouched
방법.
예를 들어 다음과 같이 사용할 수 있습니다.
form.markAllAsTouched();
다음 공식 문서를 참조하십시오.https://angular.io/api/forms/AbstractControl#markallastouched
폼 컨트롤을 루프하여 터치 마크를 붙이는 것도 가능합니다.
for(let i in this.form.controls)
this.form.controls[i].markAsTouched();
이것이 나의 해결책이다.
static markFormGroupTouched (FormControls: { [key: string]: AbstractControl } | AbstractControl[]): void {
const markFormGroupTouchedRecursive = (controls: { [key: string]: AbstractControl } | AbstractControl[]): void => {
_.forOwn(controls, (c, controlKey) => {
if (c instanceof FormGroup || c instanceof FormArray) {
markFormGroupTouchedRecursive(c.controls);
} else {
c.markAsTouched();
}
});
};
markFormGroupTouchedRecursive(FormControls);
}
이 문제가 있었지만, 지금까지 Angular 튜토리얼에는 없는 "올바른" 방법을 찾았습니다.
HTML에서form
태그, 동일한 템플릿 참조 변수 추가#myVariable='ngForm'
Reactive Forms 예제에서 사용하는 것 외에 템플릿 기반 양식 예제에서 사용하는 ('hashtag' 변수)
<form [formGroup]="myFormGroup" #myForm="ngForm" (ngSubmit)="submit()">
이것으로, 에 액세스 할 수 있게 되었습니다.myForm.submitted
대신(또는 추가) 사용할 수 있는 템플릿에서myFormGroup.controls.X.touched
:
<div *ngIf="myForm.submitted" class="text-error"> <span *ngIf="myFormGroup.controls.myFieldX.errors?.badDate">invalid date format</span> <span *ngIf="myFormGroup.controls.myFieldX.errors?.isPastDate">date cannot be in the past.</span> </div>
알아두시오myForm.form === myFormGroup
정말이야...잊지 않는 한="ngForm"
part. 를 사용하는 경우#myForm
var가 HtmlElement로 설정되기 때문에 HtmlElement를 구동하는 Directive가 아닌 HtmlElement로 설정되기 때문에 기능하지 않습니다.
알아두시오myFormGroup
는 Reactive Forms 튜토리얼에 따라 컴포넌트의 타이프스크립트 코드에 표시됩니다만,myForm
않아요, 한. 예를 들어 메서드 호출을 통해 전달하지 않는 한submit(myForm)
로로 합니다.submit(myForm: NgForm): void {...}
((림)))NgForm
입니다.)
onSubmit(form: any): void {
if (!this.form) {
this.form.markAsTouched();
// this.form.markAsDirty(); <-- this can be useful
}
}
같은 문제가 발생했지만 이 문제를 처리하는 코드로 컴포넌트를 오염시키고 싶지 않습니다.특히 저는 여러 형태로 이것이 필요하고 여러 번 반복하고 싶지 않기 때문입니다.
그래서 지시문을 작성했습니다(지금까지 게재된 답변으로).의 'NgForm'을합니다.onSubmit
- : - 방 :하지 않은 경우 제출을합니다.양식이 비활성화되면 모든 필드가 터치된 것으로 표시되며 제출이 중단됩니다.submit-Method on-Submit-Method로 설정합니다.
import {Directive, Host} from '@angular/core';
import {NgForm} from '@angular/forms';
@Directive({
selector: '[appValidateOnSubmit]'
})
export class ValidateOnSubmitDirective {
constructor(@Host() form: NgForm) {
const oldSubmit = form.onSubmit;
form.onSubmit = function (): boolean {
if (form.invalid) {
const controls = form.controls;
Object.keys(controls).forEach(controlName => controls[controlName].markAsTouched());
return false;
}
return oldSubmit.apply(form, arguments);
};
}
}
사용방법:
<form (ngSubmit)="submit()" appValidateOnSubmit>
<!-- ... form controls ... -->
</form>
이것이 제가 실제로 사용하고 있는 코드입니다.
validateAllFormFields(formGroup: any) {
// This code also works in IE 11
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.validateAllFormFields(control);
} else if (control instanceof FormArray) {
this.validateAllFormFields(control);
}
});
}
이 코드는 유효합니다.
markAsRequired(formGroup: FormGroup) {
if (Reflect.getOwnPropertyDescriptor(formGroup, 'controls')) {
(<any>Object).values(formGroup.controls).forEach(control => {
if (control instanceof FormGroup) {
// FormGroup
markAsRequired(control);
}
// FormControl
control.markAsTouched();
});
}
}
재귀 없는 솔루션
성능에 대해 우려하시는 분들을 위해 재귀를 사용하지 않는 솔루션을 생각해 냈습니다. 그러나 재귀는 여전히 모든 수준의 모든 제어에 걸쳐 반복됩니다.
/**
* Iterates over a FormGroup or FormArray and mark all controls as
* touched, including its children.
*
* @param {(FormGroup | FormArray)} rootControl - Root form
* group or form array
* @param {boolean} [visitChildren=true] - Specify whether it should
* iterate over nested controls
*/
public markControlsAsTouched(rootControl: FormGroup | FormArray,
visitChildren: boolean = true) {
let stack: (FormGroup | FormArray)[] = [];
// Stack the root FormGroup or FormArray
if (rootControl &&
(rootControl instanceof FormGroup || rootControl instanceof FormArray)) {
stack.push(rootControl);
}
while (stack.length > 0) {
let currentControl = stack.pop();
(<any>Object).values(currentControl.controls).forEach((control) => {
// If there are nested forms or formArrays, stack them to visit later
if (visitChildren &&
(control instanceof FormGroup || control instanceof FormArray)
) {
stack.push(control);
} else {
control.markAsTouched();
}
});
}
}
이 솔루션은 Form Group과 Form Array 모두에서 작동합니다.
여기서 놀 수 있다: 만지면 각진 마크
@masterwork에 따라
각도 버전 8의 typscript 코드
private markFormGroupTouched(formGroup: FormGroup) {
(Object as any).values(formGroup.controls).forEach(control => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
}); }
내가 하는 방법은 이렇다.제출 버튼을 누르거나 양식을 터치할 때까지 오류 필드를 표시하지 않도록 합니다.
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {OnInit} from "@angular/core";
export class MyFormComponent implements OnInit {
doValidation = false;
form: FormGroup;
constructor(fb: FormBuilder) {
this.form = fb.group({
title: ["", Validators.required]
});
}
ngOnInit() {
}
clickSubmitForm() {
this.doValidation = true;
if (this.form.valid) {
console.log(this.form.value);
};
}
}
<form class="form-horizontal" [formGroup]="form" >
<input type="text" class="form-control" formControlName="title">
<div *ngIf="form.get('title').hasError('required') && doValidation" class="alert alert-danger">
title is required
</div>
<button (click)="clickSubmitForm()">Submit</button>
</form>
OP의 좌절은 충분히 이해합니다.다음을 사용합니다.
유틸리티 기능:
/**
* Determines if the given form is valid by touching its controls
* and updating their validity.
* @param formGroup the container of the controls to be checked
* @returns {boolean} whether or not the form was invalid.
*/
export function formValid(formGroup: FormGroup): boolean {
return !Object.keys(formGroup.controls)
.map(controlName => formGroup.controls[controlName])
.filter(control => {
control.markAsTouched();
control.updateValueAndValidity();
return !control.valid;
}).length;
}
사용방법:
onSubmit() {
if (!formValid(this.formGroup)) {
return;
}
// ... TODO: logic if form is valid.
}
이 기능은 아직 중첩된 컨트롤을 지원하지 않습니다.
이 보석을 보세요.지금까지 본 것 중 가장 우아한 해결책입니다.
풀코드
import { Injectable } from '@angular/core';
import { FormGroup } from '@angular/forms';
const TOUCHED = 'markAsTouched';
const UNTOUCHED = 'markAsUntouched';
const DIRTY = 'markAsDirty';
const PENDING = 'markAsPending';
const PRISTINE = 'markAsPristine';
const FORM_CONTROL_STATES: Array<string> = [TOUCHED, UNTOUCHED, DIRTY, PENDING, PRISTINE];
@Injectable({
providedIn: 'root'
})
export class FormStateService {
markAs (form: FormGroup, state: string): FormGroup {
if (FORM_CONTROL_STATES.indexOf(state) === -1) {
return form;
}
const controls: Array<string> = Object.keys(form.controls);
for (const control of controls) {
form.controls[control][state]();
}
return form;
}
markAsTouched (form: FormGroup): FormGroup {
return this.markAs(form, TOUCHED);
}
markAsUntouched (form: FormGroup): FormGroup {
return this.markAs(form, UNTOUCHED);
}
markAsDirty (form: FormGroup): FormGroup {
return this.markAs(form, DIRTY);
}
markAsPending (form: FormGroup): FormGroup {
return this.markAs(form, PENDING);
}
markAsPristine (form: FormGroup): FormGroup {
return this.markAs(form, PRISTINE);
}
}
/**
* Marks as a touched
* @param { FormGroup } formGroup
*
* @return {void}
*/
markFormGroupTouched(formGroup: FormGroup) {
Object.values(formGroup.controls).forEach((control: any) => {
if (control instanceof FormControl) {
control.markAsTouched();
control.updateValueAndValidity();
} else if (control instanceof FormGroup) {
this.markFormGroupTouched(control);
}
});
}
표시:
<button (click)="Submit(yourFormGroup)">Submit</button>
API
Submit(form: any) {
if (form.status === 'INVALID') {
for (let inner in details.controls) {
details.get(inner).markAsTouched();
}
return false;
}
// as it return false it breaks js execution and return
제시된 답변에 변화를 준 버전을 만들었는데, angular 버전 8보다 오래된 버전을 사용하고 계신 분들을 위해 도움이 되는 분들과 공유하고 싶습니다.
유틸리티 기능:
import {FormControl, FormGroup} from "@angular/forms";
function getAllControls(formGroup: FormGroup): FormControl[] {
const controls: FormControl[] = [];
(<any>Object).values(formGroup.controls).forEach(control => {
if (control.controls) { // control is a FormGroup
const allControls = getAllControls(control);
controls.push(...allControls);
} else { // control is a FormControl
controls.push(control);
}
});
return controls;
}
export function isValidForm(formGroup: FormGroup): boolean {
return getAllControls(formGroup)
.filter(control => {
control.markAsTouched();
return !control.valid;
}).length === 0;
}
사용방법:
onSubmit() {
if (this.isValidForm()) {
// ... TODO: logic if form is valid
}
}
각도 13:
this.form.markAsDirty();
this.form.markAllAsTouched();
@Component()
export class AppComponent {
public loginForm: FormGroup = new FormGroup({
email: new FormControl('', Validators.required),
password: new FormControl('', Validators.required)
});
public onSubmit(): void {
this.loginForm.markAllAsTouched(); // calling mark as touch every time.
if(this.loginForm.valid) { ... }
}
}
언급URL : https://stackoverflow.com/questions/40529817/reactive-forms-mark-fields-as-touched
'bestsource' 카테고리의 다른 글
XAML만 사용하여 단색 삼각형 그리기 (0) | 2023.04.29 |
---|---|
"ng-bootstrap"과 "ngx-bootstrap"의 차이점은 무엇입니까? (0) | 2023.04.29 |
SQL Azure + 'MultipleActiveResultSets=True'를 설정한 후에도 '이 명령과 연결된 열려 있는 DataReader가 이미 있습니다.'라는 오류가 표시됨 (0) | 2023.04.24 |
Azure VM의 빈 디스크 공간 모니터링 (0) | 2023.04.24 |
이클립스에서 Ctrl+Shift+F와 Ctrl+I의 차이 (0) | 2023.04.24 |