我正在尝试对使用智能管理模板(来自 wrapbootstrap 的主题)的 Angular 7 应用程序执行表单验证。
我的问题是它在第一次刷新浏览器时按预期工作,甚至当我导航到不包含其他表单的组件时。当我导航到一个组件时,问题就出现了,该组件还包括一个带有自己的验证选项的表单。
此外,表单的实际“有效性”状态仍按预期工作。它只是没有在表单上显示引导类和消息。
我尝试过重置表单,重置任何异步/非异步验证器以及我能想到的任何其他东西。
最后,在组件之间的导航过程中没有错误或任何东西。
这是我处理导航的主要模块(main.routing.ts):
import { Routes, RouterModule } from '@angular/router';
import { ModuleWithProviders } from "@angular/core";
import { MainLayoutComponent } from '@app/shared/layout/app-layouts/main-layout.component';
import { MainComponent } from "./main.component";
import { SettingsComponent } from '../settings/settings.component';
import { GetAccountsComponent } from '../administration/get-accounts/get-accounts.component';
import { GetUsersComponent } from '../administration/get-users/get-users.component';
import { CreateAccountComponent } from '../administration/create-account/create-account.component';
import { CreateUserComponent } from '../administration/create-user/create-user.component';
import { GetTeamsComponent } from '../user/get-teams/get-teams.component';
import { GetGamesComponent } from '../user/get-games/get-games.component';
import { CreateTeamComponent } from '../game/create-team/create-team.component';
import { CreateRoundComponent } from '../game/create-round/create-round.component';
import { CreateRoundBetComponent } from '../game/create-round-bet/create-round-bet.component';
import { CreateGameComponent } from '../game/create-game/create-game.component';
export const mainRoutes: Routes = [
{
path: '',
component: MainLayoutComponent,
children: [
{
path: "",
redirectTo: "dashboard",
pathMatch: "full"
},
{
path: "dashboard",
component: MainComponent,
data: { pageTitle: "Dashboard" }
},
{
path: "settings",
component: SettingsComponent,
data: { pageTitle: "Settings" }
},
{
path: "administration/getusers",
component: GetUsersComponent,
data: { pageTitle: "Get Users" }
},
{
path: "administration/getaccounts",
component: GetAccountsComponent,
data: { pageTitle: "Get Accounts" }
},
{
path: "administration/createaccount",
component: CreateAccountComponent,
data: { pageTitle: "Create Account" }
},
{
path: "administration/createuser",
component: CreateUserComponent,
data: { pageTitle: "Create User" }
},
{
path: "user/getteams",
component: GetTeamsComponent,
data: { pageTitle: "Get Teams" }
},
{
path: "user/getgames",
component: GetGamesComponent,
data: { pageTitle: "Get Games" }
},
{
path: "game/createteam",
component: CreateTeamComponent,
data: { pageTitle: "Create Team" }
},
{
path: "game/createround",
component: CreateRoundComponent,
data: { pageTitle: "Create Round" }
},
{
path: "game/createroundbet",
component: CreateRoundBetComponent,
data: { pageTitle: "Create Round Bet" }
},
{
path: "game/creategame",
component: CreateGameComponent,
data: { pageTitle: "Create Game" }
}
]
}
];
export const mainRouting: ModuleWithProviders = RouterModule.forChild(mainRoutes);
这是一个表单示例(create-team.component.html):
<form id="checkout-form"
name="createTeamForm"
class="smart-form"
[saUiValidate]="validationOptions"
novalidate="novalidate"
[formGroup]="createTeamForm"
(ngSubmit)="onSubmit()">
<fieldset>
<div class="row">
<section class="col col-4">
<label class="select">
<select name="firstPerson"
formControlName="firstPerson">
<option value="0"
selected=""
disabled="">First Person</option>
<option value="{{user.id}}"
*ngFor="let user of users">{{user.email}}</option>
</select> <i></i> </label>
</section>
<section class="col col-4">
<label class="select">
<select name="secondPerson"
formControlName="secondPerson">
<option value="0"
selected=""
disabled="">Second Person</option>
<option value="{{user.id}}"
*ngFor="let user of users">{{user.email}}</option>
</select> <i></i> </label>
</section>
</div>
</fieldset>
<footer>
<button type="submit"
class="btn btn-primary">
Create Team
</button>
</footer>
</form>
以及包含验证选项的 .ts 文件:
import { Component, OnInit } from '@angular/core';
import { ApiService } from '@app/core/services';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';
@Component({
selector: 'app-create-team',
templateUrl: './create-team.component.html',
styleUrls: ['./create-team.component.css']
})
export class CreateTeamComponent implements OnInit {
public teamCreateSuccess: boolean;
public users: any;
public hasSubmitted: boolean;
public errorMessage: string;
public successMessage: string;
public validationOptions = {
rules: {
firstPerson: {
required: true
},
secondPerson: {
required: true
}
},
// Messages for form validation
messages: {
firstPerson: {
required: 'Please select the first person'
},
secondPerson: {
required: 'Please select the second person'
},
},
submitHandler: this.onSubmit
};
createTeamForm: FormGroup
constructor(
private apiService: ApiService
) { }
ngOnInit() {
console.log('Create Team Loaded');
this.apiService.userGetUsers()
.subscribe(
data => {
this.users = data;
// console.log(this.roles);
},
error => {
this.onCreateTeamError(error);
}
);
this.teamCreateSuccess = null;
this.hasSubmitted = null;
this.createTeamForm = new FormGroup({
firstPerson: new FormControl('0', Validators.required),
secondPerson: new FormControl('0', Validators.required),
});
}
onSubmit() {
this.hasSubmitted = true;
if (this.createTeamForm) {
console.log(this.createTeamForm.status);
console.log(this.createTeamForm.controls);
if (this.createTeamForm.status == 'VALID') {
this.apiService.createTeam(this.createTeamForm).pipe(first())
.subscribe(
data => {
this.onCreateTeamSuccess(data);
},
error => {
//console.log("failed identity check");
this.onCreateTeamError(error);
//console.log(error);
}
);
}
}
}
onCreateTeamSuccess(data: any) {
this.teamCreateSuccess = true;
this.successMessage = "Successfully created team with Id: " + data.id;
console.log(data);
}
onCreateTeamError(error: any) {
this.teamCreateSuccess = false;
this.errorMessage = "Failed to create team due to error: " + error.error;
console.log(error);
}
}
编辑:为了提供更多见解,这里是 SmartAdmin 模板正在使用的自定义验证:
import { Directive, Input, ElementRef } from "@angular/core";
@Directive({
selector: "[saUiValidate]"
})
export class UiValidateDirective {
@Input() saUiValidate: any;
constructor(private el: ElementRef) {
Promise.all([
import("jquery-validation/dist/jquery.validate.js"),
import("jquery-validation/dist/additional-methods.js")
])
.then(() => {
this.attach();
});
}
attach() {
const $form = $(this.el.nativeElement);
const validateCommonOptions = {
rules: {},
messages: {},
errorElement: "em",
errorClass: "invalid",
highlight: (element, errorClass, validClass) => {
$(element)
.addClass(errorClass)
.removeClass(validClass);
$(element)
.parent()
.addClass("state-error")
.removeClass("state-success");
},
unhighlight: (element, errorClass, validClass) => {
$(element)
.removeClass(errorClass)
.addClass(validClass);
$(element)
.parent()
.removeClass("state-error")
.addClass("state-success");
},
errorPlacement: (error, element) => {
if (element.parent(".input-group").length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
}
};
$form
.find("[data-smart-validate-input], [smart-validate-input]")
.each(function() {
var $input = $(this),
fieldName = $input.attr("name");
validateCommonOptions.rules[fieldName] = {};
if ($input.data("required") != undefined) {
validateCommonOptions.rules[fieldName].required = true;
}
if ($input.data("email") != undefined) {
validateCommonOptions.rules[fieldName].email = true;
}
if ($input.data("maxlength") != undefined) {
validateCommonOptions.rules[fieldName].maxlength = $input.data(
"maxlength"
);
}
if ($input.data("minlength") != undefined) {
validateCommonOptions.rules[fieldName].minlength = $input.data(
"minlength"
);
}
if ($input.data("message")) {
validateCommonOptions.messages[fieldName] = $input.data("message");
} else {
Object.keys($input.data()).forEach(key => {
if (key.search(/message/) == 0) {
if (!validateCommonOptions.messages[fieldName])
validateCommonOptions.messages[fieldName] = {};
var messageKey = key.toLowerCase().replace(/^message/, "");
validateCommonOptions.messages[fieldName][
messageKey
] = $input.data(key);
}
});
}
});
$form.validate($.extend(validateCommonOptions, this.saUiValidate));
}
}
编辑2:我设法找到了解决这个问题的方法,即使我有点不喜欢它。似乎在调用自定义 UI Validation 时,组件尚未呈现(我猜它与异步运行有关)。解决方案是在验证组件中添加 0ms 的“setTimeout”,如下所示:
constructor(private el: ElementRef) {
Promise.all([
import("jquery-validation/dist/jquery.validate.js"),
import("jquery-validation/dist/additional-methods.js")
])
.then(() => {
setTimeout(_ => {
this.attach();
}, 0);
});
}
如果有人能想到更好的解决方案,将不胜感激:)
期待听到您的想法。