项目

表单验证

ABP Angular UI 中的响应式表单由 ngx-validate 进行验证,并且会根据验证规则和错误蓝本自动显示帮助文本。您无需在模板中添加任何元素或组件。该库会为您处理这一切。用户体验如下:

如何添加新的错误消息

您可以通过在根应用配置的 provideAbpThemeShared 函数内的 withValidationBluePrint 方法中传入验证选项来添加新的错误消息。

import { provideAbpThemeShared, withValidationBluePrint } from '@abp/ng.theme.shared';

export const appConfig: ApplicationConfig = {
  providers: [
    // ...
    provideAbpThemeShared(
      withValidationBluePrint({
        uniqueUsername: "::AlreadyExists[{{ username }}]"
      })
    ),
    // ...
  ],
};

或者,您也可以在根配置中直接提供 VALIDATION_BLUEPRINTS 令牌。请注意不要忘记扩展 DEFAULT_VALIDATION_BLUEPRINTS。否则,内置的 ABP 验证消息将无法工作。

import { VALIDATION_BLUEPRINTS } from "@ngx-validate/core";
import { DEFAULT_VALIDATION_BLUEPRINTS } from "@abp/ng.theme.shared";

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: VALIDATION_BLUEPRINTS,
      useValue: {
        ...DEFAULT_VALIDATION_BLUEPRINTS,
        uniqueUsername: "::AlreadyExists[{{ username }}]",
      },
    },

    // 其他提供程序
  ],
};

验证器异步验证器返回一个键与错误蓝本给定的键(此处为 uniqueUsername)相匹配的错误时,验证库将能够根据给定的键和插值参数进行本地化后显示错误消息。结果将如下所示:

在此示例中:

  • 本地化键是 ::AlreadyExists
  • 插值参数是 username
  • 本地化资源定义为 "AlreadyExists": "对不起,“{0}”已存在。"
  • 验证器应返回 { uniqueUsername: { username: "admin" } } 作为错误对象。

如何更改现有的错误消息

您可以通过在根应用配置中的 provideAbpThemeShared 中传递验证选项来覆盖现有的错误消息。假设您有一个针对必填输入的自定义本地化资源。

"RequiredInput": "哎呀!我们需要这个输入。"

要使用此消息而非内置的必填输入消息,您只需执行以下操作。

import { provideAbpThemeShared, withValidationBluePrint } from '@abp/ng.theme.shared';

export const appConfig: ApplicationConfig = {
  providers: [
     // ...
    provideAbpThemeShared(
      withValidationBluePrint({
        required: "::RequiredInput",
      })
    ),
    // ...
  ],
};

或者,您也可以在根应用配置中直接提供 VALIDATION_BLUEPRINTS 令牌。请不要忘记扩展 DEFAULT_VALIDATION_BLUEPRINTS。否则,内置的 ABP 验证消息将无法工作。

import { VALIDATION_BLUEPRINTS } from "@ngx-validate/core";
import { DEFAULT_VALIDATION_BLUEPRINTS } from "@abp/ng.theme.shared";

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: VALIDATION_BLUEPRINTS,
      useValue: {
        ...DEFAULT_VALIDATION_BLUEPRINTS,
        required: "::RequiredInput",
      },
    },

    // 其他提供程序
  ],
};

错误消息将如下所示:

如何在表单上禁用验证

如果您想手动验证表单,可以随时在其上禁用自动验证。您只需在表单元素上放置 skipValidation 属性即可。

<form [formGroup]="form" skipValidation>
  <!-- 表单字段写在这里 -->
</form>

如何在特定字段上禁用验证

验证适用于任何带有 formControlformControlName 指令的元素或组件。您可以通过在输入元素或组件上放置 skipValidation 属性来禁用特定字段的自动验证。

<input type="text" formControlName="name" skipValidation />

如何使用自定义错误组件

首先,构建一个自定义错误组件。扩展现有的 ValidationErrorComponent 会使这更容易。

import { LocalizationPipe } from "@abp/ng.core";
import { ValidationErrorComponent } from "@abp/ng.theme.basic";
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component } from "@angular/core";

@Component({
  selector: "app-validation-error",
  imports:[CommonModule, LocalizationPipe],
  template: `
    <div
      class="font-weight-bold font-italic px-1 invalid-feedback"
      *ngFor="let error of abpErrors; trackBy: trackByFn"
    >
      {{ error.message | abpLocalization: error.interpoliteParams }}
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ErrorComponent extends ValidationErrorComponent {}

然后,在您的根配置中提供它。

import { VALIDATION_ERROR_TEMPLATE } from "@ngx-validate/core";

export const appConfig: ApplicationConfig = {
  providers: [
    // 其他提供程序
    {
      provide: VALIDATION_ERROR_TEMPLATE,
      useValue: ErrorComponent,
    },
  ],
};

现在错误消息将以粗体和斜体显示:

如何验证嵌套表单组

在 ABP Angular UI 中,有几种方法可以验证嵌套的表单组。下面是第一种也是最常见的方法,即使用嵌套响应式表单进行自动验证和错误消息显示。(第二种方法将在下一节中介绍。)

第一种方式:使用嵌套响应式表单进行自动验证和错误消息显示

ABP Angular UI 利用 Angular 的响应式表单和 ngx-validate 库,提供了强大、灵活且用户友好的表单验证体验。无论您是手动构建表单还是使用 ABP 的动态表单生成功能,验证和错误消息都是自动处理的。

主要特性

  • 自动验证: 在您的 DTO 中定义的所有验证规则(例如 [Required][StringLength][EmailAddress] 等)都会自动反映在 Angular 表单中。错误消息会在每个字段下方显示,无需任何额外的标记。

  • 嵌套表单组和动态字段: 对于复杂的数据结构,您可以使用嵌套的 FormGroupFormArray 结构对字段进行分组或管理动态列表。验证和错误显示对父控件和子控件都能无缝工作。

  • 动态和可扩展的表单: 借助 ABP 的可扩展性系统,您可以使用 generateFormFromProps 等帮助程序动态生成表单,并使用 abp-extensible-form 组件来显示它们。这确保了所有实体属性(包括扩展属性)都包含在表单中,并且应用了它们的验证规则。

  • 无需额外的样板代码: 您不需要为验证添加自定义错误组件或指令。该系统开箱即用,包括嵌套和动态生成的控件。

实际示例:用户表单中的嵌套表单组

下面是 ABP Angular UI 中用户管理表单的一个真实示例,展示了嵌套表单结构和验证是如何实现的。此示例包括动态生成的字段(使用 abp-extensible-form)和使用 FormArrayFormGroup 的动态角色列表。

TypeScript: 构建表单

buildForm() {
  const data = new FormPropData(this.injector, this.selected);
  this.form = generateFormFromProps(data); // 从实体和扩展属性自动创建表单控件

  this.service.getAssignableRoles().subscribe(({ items }) => {
    this.roles = items;
    if (this.roles) {
      // 动态角色列表:嵌套的 FormArray 和 FormGroup
      this.form.addControl(
        'roleNames',
        this.fb.array(
          this.roles.map(role =>
            this.fb.group({
              [role.name as string]: [
                this.selected?.id
                  ? !!this.selectedUserRoles?.find(userRole => userRole.id === role.id)
                  : role.isDefault,
              ],
            }),
          ),
        ),
      );
    }
  });
}

HTML: 显示表单

<abp-modal [(visible)]="isModalVisible" [busy]="modalBusy">
  <ng-template #abpHeader>
    <h3>{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }</h3>
  </ng-template>

  <ng-template #abpBody>
    @if (form) {
      <form [formGroup]="form" (ngSubmit)="save()">
        <ul ngbNav #nav="ngbNav" class="nav-tabs">
          <li ngbNavItem>
            <a ngbNavLink>{ 'AbpIdentity::UserInformations' | abpLocalization }</a>
            <ng-template ngbNavContent>
              <!-- 自动显示所有实体字段及其验证 -->
              <abp-extensible-form [selectedRecord]="selected"></abp-extensible-form>
            </ng-template>
          </li>

          <li ngbNavItem>
            <a ngbNavLink>{ 'AbpIdentity::Roles' | abpLocalization }</a>
            <ng-template ngbNavContent>
              <!-- 动态角色列表:嵌套的 FormArray 和 FormGroup -->
              @for (roleGroup of roleGroups; track $index; let i = $index) {
                <div class="form-check mb-2">
                  <abp-checkbox
                    [formControl]="roleGroup.controls[roles[i].name]"
                    [label]="roles[i].name"
                  ></abp-checkbox>
                </div>
              }
            </ng-template>
          </li>
        </ul>
        <div class="mt-2 fade-in-top" [ngbNavOutlet]="nav"></div>
      </form>
    } @else {
      <div class="text-center"><i class="fa fa-pulse fa-spinner" aria-hidden="true"></i></div>
    }
  </ng-template>
</abp-modal>

说明:

  • abp-extensible-form 自动生成并显示所有实体字段及其验证。
  • 在角色选项卡中,每个角色由一个复选框表示,这些复选框在 FormArray 中管理,每个复选框都是一个 FormGroup。这是一个嵌套表单结构的实际示例。
  • 所有验证和错误消息都会为主表单和嵌套组自动显示。

第二种方式:手动创建嵌套响应式表单(不使用 abp-extensible-form)

您也可以手动构建和验证嵌套表单组,而不使用 abp-extensible-form 或动态帮助程序。这种方法让您完全控制表单结构,对于自定义或非基于实体的表单非常有用。

示例:简单的手动嵌套 FormGroup

下面是一个简单的、通用的嵌套响应式表单示例。该表单包含一个用于个人资料信息的嵌套 FormGroup,并演示了如何应用验证规则。

TypeScript: 构建表单

import { Component, OnInit, inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NgxValidateCoreModule } from '@ngx-validate/core';

@Component({
  selector: 'app-nested-form',
  templateUrl: './nested-form.component.html',
  standalone: true,
  imports: [NgxValidateCoreModule],
})
export class NestedFormComponent implements OnInit {
  form: FormGroup;

  private fb = inject(FormBuilder);

  ngOnInit() {
    this.buildForm();
  }

  buildForm() {
    this.form = this.fb.group({
      userName: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
      profile: this.fb.group({
        firstName: ['', Validators.required],
        lastName: ['', Validators.required],
      }),
    });
  }

  submit() {
    if (this.form.invalid) {
      return;
    }
    // 处理提交逻辑
  }
}

HTML: 显示表单

<form [formGroup]="form" (ngSubmit)="submit()">
  <div class="mb-3">
    <label class="form-label">用户名</label>
    <input type="text" class="form-control" formControlName="userName" />
  </div>

  <div class="mb-3">
    <label class="form-label">邮箱</label>
    <input type="email" class="form-control" formControlName="email" />
  </div>

  <div formGroupName="profile" class="card mt-3">
    <div class="card-header">
      <strong>个人资料详情</strong>
    </div>
    <div class="card-body">
      <div class="mb-3">
        <label class="form-label">名</label>
        <input type="text" class="form-control" formControlName="firstName" />
      </div>

      <div class="mb-3">
        <label class="form-label">姓</label>
        <input type="text" class="form-control" formControlName="lastName" />
      </div>
    </div>
  </div>

  <hr class="my-3" />

  <div>
    <abp-button buttonType="submit" iconClass="fa fa-save" [disabled]="form.invalid">
      保存
    </abp-button>
  </div>
</form>

工作原理:

  • 该表单包含主要字段(userNameemail)和一个嵌套的 FormGroupprofile)。
  • profile 组包含 firstNamelastName 字段,每个字段都有自己的验证规则。
  • 验证规则直接在表单构建器中定义。
  • 错误消息和验证反馈由 ngx-validate 和 ABP Angular UI 自动处理,就像动态表单一样。
  • 此结构确保验证能自动对主表单和嵌套组生效。

注意: 这种方法适用于自定义表单或当您希望完全控制表单结构时。它提供了与 ABP 动态表单类似的用户体验和验证行为,但在表单布局和逻辑上提供了手动控制。


在本文档中