昨天跟大家分享了自订表单元件的作法,但昨天的作法只适用於一个栏位、一个 FormControl
。
虽然 FormControl
里是可以设 {}
的值,但如果我们真的想要的是一个可以直接用 [formGroup]
、 [formArray]
所使用的元件呢?
没问题,只要你想要, Angular 都给你
大家还记得之前我们做了个「被保人表单」吧?
一开始只有「姓名」、「性别」跟「年龄」这三个栏位,後来我们加了「联络资讯」的栏位,这次我们再帮它加个「联络地址」的栏位吧!
一般来说,联络地址的栏位通常会分成「县市」、「乡镇市区」、「邮递区号」与「地址」,而「县市」、「乡镇市区」与「邮递区号」之间会有一些连动逻辑,县市」与「乡镇市区」这两个栏位也通常会是下拉选单,其他的则是一般的 input
栏位。
首先一样先把 HTML 准备好,像这样:
<p><label>联络地址:</label></p>
<p>
<select>
<option value="">请选择县市</option>
</select>
<select>
<option value="">请选择乡镇市区</option>
</select>
</p>
<p>
<input type="text" style="width: 4rem" placeholder="邮递区号">
<input type="text" place="请输入地址">
</p>
画面:
样式用
inline
的方式设定是方便教学,小朋友们要尽量少用噢!
接着,在 .ts
里加入地址的相关栏位的 FormGroup
与 FormControl
:
const addressInfoFormGroup = this.formBuilder.group({
city: '',
district: '',
zip: '',
address: ''
});
return this.formBuilder.group({
name: [
'',
[Validators.required, Validators.minLength(2), Validators.maxLength(10)]
],
gender: ['', Validators.required],
age: ['', Validators.required],
contactInfoType: contactInfoTypeControl,
contactInfo: contactInfoControl,
addressInfo: addressInfoFormGroup
});
然後再将其绑与画面上元素绑定,像这样:
<ng-container formGroupName="addressInfo">
<p><label>联络地址:</label></p>
<p>
<select formControlName="city">
<option value="">请选择县市</option>
</select>
<select formControlName="district">
<option value="">请选择乡镇市区</option>
</select>
</p>
<p>
<input type="text" style="width: 4rem" placeholder="邮递区号" formControlName="zip">
<input type="text" placeholder="请输入地址" formControlName="address">
</p>
</ng-container>
连动逻辑的实作就交给大家练习罗,我们今天没有要着重於此部分的处理。
至此,我们就完成了第一步的准备工作。
接下来,我们就要将联络地址这块拆成一个独立的 Component ─ AddressInfoComponent
。
首先,先将 HTML 搬过去并稍微调整一下:
<ng-container [formGroup]="formGroup">
<p>
<select formControlName="city">
<option value="">请选择县市</option>
</select>
<select formControlName="district">
<option value="">请选择乡镇市区</option>
</select>
</p>
<p>
<input type="text" style="width: 4rem" placeholder="邮递区号" formControlName="zip">
<input type="text" placeholder="请输入地址" formControlName="address">
</p>
</ng-container>
接着在 AddressInfoComponent
里注入 ControlContainer
:
export class AddressInfoComponent {
constructor(private controlContainer: ControlContainer) { }
}
然後加上:
get formGroup(): FormGroup {
return this.controlContainer.control as FormGroup;
}
再回到被保人表单里,把原本的联络地址区块改成:
<p><label>联络地址:</label></p>
<app-address-info formGroupName="addressInfo"></app-address-info>
至此就大功告成了!是不是超简单的?!
不过之所以这麽简单是因为这是 Reactive Forms 的方式,今天的 ControlContainer
不像昨天的 ControlValueAccessor
可以做一次之後,两种方式都可以使用。
如果今天这个元件是要让 Template Driven Forms 使用的话,首先要先将 Template 原本用 Reactive Forms 的绑定方式改成使用 Template Driven Forms 的绑定方式,像是这样:
<ng-container ngModelGroup="addressInfo">
<p>
<select name="zip" ngModel>
<option value="">请选择县市</option>
</select>
<select name="district" >
<option value="">请选择乡镇市区</option>
</select>
</p>
<p>
<input type="text" style="width: 4rem" placeholder="邮递区号" name="zip" ngModel>
<input type="text" placeholder="请输入地址" name="address" ngModel>
</p>
</ng-container>
然後也不用在 AddressInfoComponent
里注入 ControlContainer
,而是改在 AddressInfoComponent
的 MetaData 的 viewProviders
里新增以下设定:
@Component({
selector: 'app-address-info',
templateUrl: './address-info.component.html',
styleUrls: ['./address-info.component.scss'],
viewProviders:[
{
provide: ControlContainer,
useExisting: NgForm
}
]
})
export class AddressInfoComponent {
这样就能直接用 <app-address-info></app-address-info>
的方式使用这个元件了。
大家觉得,是 Reactive Forms
的方式好用,还是 Template Driven Forms
的方式好用呢?
今天的重点主要是让大家知道要怎麽使用 ControlContainer
这个类别来包装我们的元件,以达到提昇重用性与维护性的目的。
虽然麻烦的是,它没办法像昨天分享的 ControlValueAccessor
一样,做好了之後可以适用於 Template Driven Forms 与 Reactive Forms ,但好在它的用法其实颇为简单,主要的差异就只有在 Template Driven Forms 需要靠 viewProvider
,而 Reactive Froms 只要注入就行。
关於 viewProvider 与 provider 的差异,我推荐大家可以去看 Kevin (台湾 Angular GDE)的 [Angular] viewProviders V.S. providers ,我觉得写得非常的清楚。
此外,如果觉得我分享不好,也可以参考 Kevin 的 [Angular] ControlContainer 的应用
今天的程序码会放在 Github - Branch: day29 上供大家参考,建议大家在看我的实作之前,先按照需求规格自己做一遍,之後再跟我的对照,看看自己的实作跟我的实作不同的地方在哪里、有什麽好处与坏处,如此反覆咀嚼消化後,我相信你一定可以进步地非常快!
如果有任何的问题或是回馈,还请麻烦留言给我让我知道!
<<: 29 - 有效的使用 Observability 的资料 (3) - 资料的生命周期管理
前言 走过了资料分析、演算法选择後, 我们得知了有些可以改善模型的方向: 解决资料不平衡(Done)...
题组回顾与观念统整 这一段我们着重在「动态规划」优化,如何从穷举或递回的方法中进一步地将结果记录下...
函式会将参数传入函式里面,让它们成为函式里的变数,让程序码去做运算。参数只能在函式里刷存在感(期间...
今天我们要来研究 line-liff-v2-starter 里面写了些什麽,这对之後想开发自己的 L...
思考重点 封包是如何找到下一个端节点的 IP地址在封包转发中扮演的角色 MAC地址在封包转发中扮演的...