Angular响应式表单与Bootstrap – 自定义验证在动画中不起作用

huangapple go评论90阅读模式
英文:

Angular Reactive Form with Bootstrap - Custom validation is not working in animation

问题

我在Angular 16中创建了一个响应式表单,并为其添加了Bootstrap验证。内置的验证器正常工作,但添加自定义验证器后,错误会添加到errors数组中,但Bootstrap仍然将输入元素显示为有效。

bootstrap-form.component.html:

<div class="container">
	<div class="row">
		<div class="col">
			<h2 class="text-center fs-3 semibold">
				{{ loginForm.value | json }}
			</h2>
			<form class="needs-validation" [formGroup]="loginForm" novalidate>
				<div class="mt-4">
					<label for="username-input" class="form-label fs-4">
						username
					</label>
					<input
						type="text"
						id="username-input"
						placeholder="username"
						class="form-control mt-2"
						formControlName="username"
						required
					/>
					<div
						class="invalid-feedback"
						*ngIf="loginForm.controls['username'].hasError('required')"
					>
						username cannot be empty
					</div>
				</div>
				<div class="mt-4">
					<label for="password-input" class="form-label fs-4">
						password
					</label>
					<input
						type="password"
						id="password-input"
						placeholder="password"
						class="form-control mt-2"
						formControlName="password"
						required
					/>
					<div
						class="invalid-feedback"
						*ngIf="loginForm.controls['password'].hasError('required')"
					>
						password cannot be empty
					</div>
					<div
						class="invalid-feedback"
						*ngIf="loginForm.controls['password'].errors?.['passwordInvalid']"
					>
						password cannot be less than 8 characters
					</div>
					<h3 class="fs-6">
						{{ loginForm.controls["password"].errors | json }}
					</h3>
				</div>
				<div class="mt-4">
					<button type="submit" class="btn btn-primary col-12">
						login
					</button>
				</div>
			</form>
		</div>
	</div>
</div>

bootstrap-form.component.ts:

import { Component, OnInit } from '@angular/core';
import {
	AbstractControl,
	FormBuilder,
	FormControl,
	FormGroup,
	Validators,
} from '@angular/forms';

@Component({
	selector: 'app-bootstrap-form',
	templateUrl: './bootstrap-form.component.html',
	styleUrls: ['./bootstrap-form.component.css'],
})
export class BootstrapFormComponent implements OnInit {
	loginForm: FormGroup;

	constructor(private formBuilderService: FormBuilder) {
		this.loginForm = this.formBuilderService.group({
			username: ['', [Validators.required]],
			password: ['', [Validators.required, validatePassword]],
			phoneNumber: ['', [Validators.required]],
		});
	}

	ngOnInit(): void {
		let form = document.querySelector('form') as HTMLFormElement;
		form.addEventListener('submit', (submitEvent: SubmitEvent) => {
			if (!form.checkValidity()) {
				submitEvent.preventDefault();
				submitEvent.stopPropagation();
			}

			form.classList.add('was-validated');
		});
	}
}

export function validatePassword(
	formControl: AbstractControl
): { [key: string]: any } | null {
	if (formControl.value && formControl.value.length < 8) {
		return { passwordInvalid: true };
	}
	return null;
}

如附加的截图所示,errors数组有一个错误,但Bootstrap仍然显示为绿色。

我刚刚尝试了这段代码,但我不明白问题出在哪里,所以不知道要尝试什么。

英文:

I created a Reactive Form in Angular 16, and added Bootstrap validation to it, normal built-in validators work fine, adding a custom validator also adds the error to the errors array but Bootstrap still shows the input element as valid.

bootstrap-form.component.html:

&lt;div class=&quot;container&quot;&gt;
	&lt;div class=&quot;row&quot;&gt;
		&lt;div class=&quot;col&quot;&gt;
			&lt;h2 class=&quot;text-center fs-3 semibold&quot;&gt;
				{{ loginForm.value | json }}
			&lt;/h2&gt;
			&lt;form class=&quot;needs-validation&quot; [formGroup]=&quot;loginForm&quot; novalidate&gt;
				&lt;div class=&quot;mt-4&quot;&gt;
					&lt;label for=&quot;username-input&quot; class=&quot;form-label fs-4&quot;&gt;
						username
					&lt;/label&gt;
					&lt;input
						type=&quot;text&quot;
						id=&quot;username-input&quot;
						placeholder=&quot;username&quot;
						class=&quot;form-control mt-2&quot;
						formControlName=&quot;username&quot;
						required
					/&gt;
					&lt;div
						class=&quot;invalid-feedback&quot;
						*ngIf=&quot;
							loginForm.controls[&#39;username&#39;].hasError(&#39;required&#39;)
						&quot;
					&gt;
						username cannot be empty
					&lt;/div&gt;
				&lt;/div&gt;
				&lt;div class=&quot;mt-4&quot;&gt;
					&lt;label for=&quot;password-input&quot; class=&quot;form-label fs-4&quot;&gt;
						password
					&lt;/label&gt;
					&lt;input
						type=&quot;password&quot;
						id=&quot;password-input&quot;
						placeholder=&quot;password&quot;
						class=&quot;form-control mt-2&quot;
						formControlName=&quot;password&quot;
						required
					/&gt;
					&lt;div
						class=&quot;invalid-feedback&quot;
						*ngIf=&quot;
							loginForm.controls[&#39;password&#39;].hasError(&#39;required&#39;)
						&quot;
					&gt;
						password cannot be empty
					&lt;/div&gt;
					&lt;div
						class=&quot;invalid-feedback&quot;
						*ngIf=&quot;
							loginForm.controls[&#39;password&#39;].errors?.[&#39;passwordInvalid&#39;]
						&quot;
					&gt;
						password cannot be less than 8 characters
					&lt;/div&gt;
					&lt;h3 class=&quot;fs-6&quot;&gt;
						{{ loginForm.controls[&quot;password&quot;].errors | json }}
					&lt;/h3&gt;
				&lt;/div&gt;
				&lt;div class=&quot;mt-4&quot;&gt;
					&lt;button type=&quot;submit&quot; class=&quot;btn btn-primary col-12&quot;&gt;
						login
					&lt;/button&gt;
				&lt;/div&gt;
			&lt;/form&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;

bootstrap-form.component.ts:

import { Component, OnInit } from &#39;@angular/core&#39;;
import {
	AbstractControl,
	FormBuilder,
	FormControl,
	FormGroup,
	Validators,
} from &#39;@angular/forms&#39;;

@Component({
	selector: &#39;app-bootstrap-form&#39;,
	templateUrl: &#39;./bootstrap-form.component.html&#39;,
	styleUrls: [&#39;./bootstrap-form.component.css&#39;],
})
export class BootstrapFormComponent implements OnInit {
	loginForm: FormGroup;

	constructor(private formBuilderService: FormBuilder) {
		this.loginForm = this.formBuilderService.group({
			username: [&#39;&#39;, [Validators.required]],
			password: [&#39;&#39;, [Validators.required, validatePassword]],
			phoneNumber: [&#39;&#39;, [Validators.required]],
		});
	}

	ngOnInit(): void {
		let form = document.querySelector(&#39;form&#39;) as HTMLFormElement;
		form.addEventListener(&#39;submit&#39;, (submitEvent: SubmitEvent) =&gt; {
			if (!form.checkValidity()) {
				submitEvent.preventDefault();
				submitEvent.stopPropagation();
			}

			form.classList.add(&#39;was-validated&#39;);
		});
	}
}

export function validatePassword(
	formControl: AbstractControl
): { [key: string]: any } | null {
	if (formControl.value &amp;&amp; formControl.value.length &lt; 8) {
		return { passwordInvalid: true };
	}
	return null;
}

Angular响应式表单与Bootstrap – 自定义验证在动画中不起作用
Angular响应式表单与Bootstrap – 自定义验证在动画中不起作用

As you can see in the attached screenshots, the errors array has a nerror but Bootstrap still shows it in green.

I just tried out this code, and I can't understand what's wrong here so I don't know what to try out.

答案1

得分: 1

根据Bootstrap文档中的表单验证

> 所有现代浏览器都支持约束验证API,这是一系列用于验证表单控件的JavaScript方法。

尽管响应式表单对password字段抛出错误,但它没有在约束验证API中设置错误。

方法1:使用minLength属性

validatePassword函数中,您正在验证密码的最小长度,您可以在<input>元素中添加minLength="8"属性。

<input
    type="password"
    id="password-input"
    placeholder="password"
    class="form-control mt-2"
    formControlName="password"
    required
    minlength="8"
/>

请注意,您可以将validatePassword替换为Validators.minLength(8)以进行表单控件验证

password: ['', [Validators.required, Validators.minLength(8)]]

方法2:更新验证API的错误消息

如果您希望在不使用约束验证API的HTML属性的情况下使用Angular响应式表单内置/自定义验证,您需要通过setCustomValidity(error)为每个<input>元素更新约束验证API中的错误消息。

<input
    #passwordInput
    type="password"
    id="password-input"
    placeholder="password"
    class="form-control mt-2"
    formControlName="password"
    required
    (input)="validatePasswordInput(passwordInput)"
/>
validatePasswordInput(passwordField: HTMLInputElement) {
  if (this.loginForm.controls['password'].errors) {
    for (let error in this.loginForm.controls['password'].errors)
      passwordField.setCustomValidity(
        this.loginForm.controls['password'].errors[error]
      );
  } else {
    // No error
    passwordField.setCustomValidity('');
  }
}

在 StackBlitz 上查看演示

英文:

According to Form validation in Bootstrap docs,

> All modern browsers support the constraint validation API, a series of JavaScript methods for validating form controls.

Although the Reactive form throws an error for the password field, it doesn't set the error in the constraint validation API.


Approach 1: Use minLength attribute

From the validatePassword function, you are validating the password minimum length, you can add the minLength=&quot;8&quot; attribute to the &lt;input&gt; element.

&lt;input
    type=&quot;password&quot;
    id=&quot;password-input&quot;
    placeholder=&quot;password&quot;
    class=&quot;form-control mt-2&quot;
    formControlName=&quot;password&quot;
    required
    minlength=&quot;8&quot;
/&gt;

Note that, you can replace the validatePassword with Validators.minLength(8) for the form control validation

password: [&#39;&#39;, [Validators.required, Validators.minLength(8)]]

Approach 2: Update the error message to Validation API

If you are keen to use the Angular Reactive Form built-in/custom validation without the HTML attribute for the constraint validation API, you need to update the error message in the constraint validation API for each &lt;input&gt; element via setCustomValidity(error).

&lt;input
    #passwordInput
    type=&quot;password&quot;
    id=&quot;password-input&quot;
    placeholder=&quot;password&quot;
    class=&quot;form-control mt-2&quot;
    formControlName=&quot;password&quot;
    required
    (input)=&quot;validatePasswordInput(passwordInput)&quot;
/&gt;
validatePasswordInput(passwordField: HTMLInputElement) {
  if (this.loginForm.controls[&#39;password&#39;].errors) {
    for (let error in this.loginForm.controls[&#39;password&#39;].errors)
      passwordField.setCustomValidity(
        this.loginForm.controls[&#39;password&#39;].errors[error]
      );
  } else {
    // No error
    passwordField.setCustomValidity(&#39;&#39;);
  }
}

Demo @ StackBlitz

huangapple
  • 本文由 发表于 2023年6月29日 20:53:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76581257.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定