使用Angular将每个表单作为子组件进行拆分

首先

为了提高重用性和可维护性,我们将把每个表单项作为组件进行切割。

经过

“I previously posted this article and incorporated this approach into our new service on site, but this later became a problem…”

    • 条件分岐をうまくやらないと同じ文言が複数表示されてしまうため、条件が複雑になりがちである。

 

    共通化してしまっているのでバリデーションが増えるとほかのフォームのテストもしなければいけなくなる。

这两个问题成为了问题。

经过思考,我得出了这样一个结论:如果把每个表单切分为组件,那就能够变得更加幸福。于是,我决定采用现在的形式。

现在我们开始实际进行切割。

前提条件 (Qiantí

表單的設計將使用Angular Material。我們將在預設已完成設定等的情況下進行。同時我們將使用響應式表單來進行實現。

文件夹结构

undefined

实施

app.module.ts 文件app.module.ts 文件
import { BrowserModule } from ‘@angular/platform-browser’;
import { NgModule } from ‘@angular/core’;
import { ReactiveFormsModule } from ‘@angular/forms’;
import { BrowserAnimationsModule } from ‘@angular/platform-browser/animations’;
import { MatButtonModule, MatFormFieldModule, MatInputModule, MatListModule } from ‘@angular/material’;

import { AppComponent } from ‘./app.component’;
import { UserIdComponent } from ‘./form-components/user-id/user-id.component’;
import { PasswordComponent } from ‘./form-components/password/password.component’;
import { UserNameComponent } from ‘./form-components/user-name/user-name.component’;

@NgModule({
declarations: [
AppComponent,
UserIdComponent,
PasswordComponent,
UserNameComponent
],
imports: [
BrowserModule,
ReactiveFormsModule,
BrowserAnimationsModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatListModule,
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {
}

 

base-form.ts这是一个从ErrorStateMatcher的示例中提取必要部分的base-form类。

base-form.ts
import { ErrorStateMatcher } from “@angular/material”;
import { AbstractControl } from “@angular/forms”;

export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: AbstractControl | null): boolean {
return !!(control && control.invalid && (control.dirty || control.touched));
}
}

export class BaseForm {
matcher = new MyErrorStateMatcher();
}

 

用户ID表单组件在组件生成时,将此表单组件自身发送给父组件。

user-id.ts
import { Component, EventEmitter, OnInit, Output } from ‘@angular/core’;
import { BaseForm } from “../base-form”;
import { AbstractControl, FormControl, Validators } from “@angular/forms”;

@Component({
selector: ‘user-id’,
templateUrl: ‘./user-id.component.html’,
styleUrls: [ ‘./user-id.component.css’ ]
})
// 继承BaseForm以使用ErrorStateMatcher
export class UserIdComponent extends BaseForm implements OnInit {
// 用于将此表单组件发送给父组件的@Output()
@Output() formReady = new EventEmitter();
// 定义表单
form: AbstractControl = new FormControl(”, [ Validators.required ]);

constructor() {
super();
}

// 在组件生成时将表单组件发送给父组件
ngOnInit() {
this.formReady.emit(this.form);
}
}

只需根据Angular Material的要求在视图中实现即可。
将[formControl]指定为在组件类中声明的FormControl。
将[errorStateMatcher]指定为base-form.ts的Matcher。
然后只需要实现验证消息。

user-id.html

用户ID

必填项。

 

在父组件中,我们需要声明一个FormGroup并实现formInitialized()函数来设置FormGroup并将每个表单组件添加到其中。只需通过从视图中调用它,就可以设置每个表单组件。可以通过以下方式获取值:

this.FormGroup.get(‘key’).value

app.component.ts:

import { Component } from ‘@angular/core’;
import { AbstractControl, FormBuilder, FormGroup } from “@angular/forms”;

@Component({
selector: ‘app-root’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ]
})
export class AppComponent{
// 用于设置每个表单组件的FormGroup
myForm: FormGroup;

// 在初始化时实例化FormGroup
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({});
}

// 将从子组件接收到的表单添加到父FormGroup
formInitialized(name: string, form: AbstractControl) {
this.myForm.setControl(name, form);
}

showValue() {
// 可以通过this.formGroup.get(‘key’).value来获取值。
console.log(`用户ID … ${this.myForm.get(‘userId’).value}`);
console.log(`用户名 … ${this.myForm.get(‘userName’).value}`);
console.log(`密码 … ${this.myForm.get(‘password’).value}`);
}
}

在视图中,我们通过@Output()从子组件接收通过emit传递的值,并将其与键一起传递给父组件的formInitialized()函数。通过这样做,它就会被设置到父组件的FormGroup中。
※user-name,password的实现与user-id几乎相同

app.component.html:

会员注册

确认行动

undefined

请总结。

通过将其作为组件划分出来,即使在每个表单中改变行为,也几乎不会对其他表单产生影响。
而且,只需修改一个表单组件,就可以反映到其他使用的地方,从而使实现变得非常简单。

最后

“Angular真是让人头痛啊…”

参考链接

具有 Angular 组件的部分响应式表单

广告
将在 10 秒后关闭
bannerAds