angular2 表单拆成多个组件及提交验证问题
angular2表单最常用的方法就是在input或者textarea里直接添加formControlName或者formGroupName进行数据双向绑定并验证。
1 <form [formGroup]="goodsFormInfo"> 2 <input type="text" formControlName="book_file" readonly="" /> 3 <!--或者--> 4 <dl formGroupName="book_isbn"> 5 <dt> 6 <span>*</span>ISBN号: 7 </dt> 8 <dd> 9 <input id="book_isbn1" td-focus="" formControlName="book_isbn1" [patterninput]="'^[0-9]+$'" class="isbn" type="text" maxlength="3" /> 10 <span>-</span> 11 <input id="book_isbn2" td-focus="" formControlName="book_isbn2" [patterninput]="'^[0-9]+$'" class="isbn" type="text" maxlength="1" /> 12 <span>-</span> 13 <input id="book_isbn3" td-focus="" formControlName="book_isbn3" [patterninput]="'^[0-9]+$'" class="isbn" type="text" maxlength="7" /> 14 <span>-</span> 15 <input id="book_isbn4" td-focus="" formControlName="book_isbn4" [patterninput]="'^[0-9]+$'" class="isbn" type="text" maxlength="6" /> 16 <span>-</span> 17 <input id="book_isbn5" td-focus="" formControlName="book_isbn5" [patterninput]="'^[0-9]+$'" class="isbn" type="text" maxlength="1" /> 18 <p class="warn-mark" *ngIf="mesg('book_isbn1') || mesg('book_isbn2') || mesg('book_isbn3') || mesg('book_isbn4') || mesg('book_isbn5') || mesg('book_isbn')"> {{ mesg('book_isbn1') || mesg('book_isbn2') || mesg('book_isbn3') || mesg('book_isbn4') || mesg('book_isbn5') || mesg('book_isbn') }}</p> 19 </dd> 20 </dl> 21 </form>
不过最近遇到一个比较复杂的表单提交, 如果全部写在一个组件里,代码可读性太差,所以把表单拆成了多个组件,通过formControlName进行数据的双向绑定。
举个简单的例子(核心代码):
一、form表单-html代码
1 <dl> 2 <dt> 商品定时下架:</dt> 3 <dd> 4 <tl-goods-sold-out-timing formControlName="auto_off_date"></tl-goods-sold-out-timing> 5 <p class="color-grey">系统会在该时间自动执行下架</p> 6 <p class="warn-mark warn-date" *ngIf="mesg('auto_off_date')">{{mesg('auto_off_date')}}</p> 7 </dd> 8 </dl> 9 <button class="btn btn-blue" (click)="submit()">保存</button>
二、tl-goods-sold-out-timing子组件 - html代码
1 <div class="clearfix"> 2 <tl-check-box [labelname]="'设定'" [(ngmodel)]="isChecking" (onselectedfn)="selectedFn($event)"></tl-check-box> 3 <div class="position-relative"> 4 <tl-calendar [calendarobj]="calendarObj" [(ngModel)]="startDate" (ngModelChange)="selectDate($event)"></tl-calendar> 5 </div> 6 </div>
看到这里一定很奇怪,为什么同时写了ngModel和ngModelChange,ngModelChange在ts文件里会用到,这个日历组件的值改变后会需要我们调用一个change事件的,往下看。
三、tl-goods-sold-out-timing子组件 - ts代码
import { Component, OnInit, Output, Input, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { CalendarObj } from '../../../../../shared'
@Component({
selector: 'tl-goods-sold-out-timing',
templateUrl: './goods-sold-out-timing.component.html',
styleUrls: ['./goods-sold-out-timing.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => GoodsSoldOutTimingComponent),
multi: true
}
]
})
export class GoodsSoldOutTimingComponent implements OnInit {
public calendarObj: CalendarObj;
public isChecking: boolean;
public startDate: Date;
private change = (value) => { }; // 划重点
constructor() {
this.calendarInit();
}
ngOnInit() { }
get auto_off_date() { // 父组件的formControlName接收这块return的数据 get的auto_off_date名称和下面this.change(this.auto_off_date);括号中的值名称一样
console.log(this.startDate);
if (!this.isChecking) {
return {
date: '',
set: this.isChecking
}
}
return {
date: this.startDate ? this.formatDate(this.startDate) : '',
set: this.isChecking
}
}
registerOnChange(fn) {
this.change = fn;
}
registerOnTouched(fn) { }
writeValue(value) { // 初始时如果父组件有值传入,就从这里写入组件。
console.log(value);
if (!value) {
this.isChecking = false;
} else {
this.startDate = new Date(value.substr(0, 10)); // startDate为日历组件数据de双向绑定
this.isChecking = true;
console.log(this.startDate);
}
}
// check-box事件
selectedFn(event) {
this.change(this.auto_off_date);
}
// 日期改变
selectDate(date) { // 数据一但改变,就调用一次change事件,如果上面html文件中没有写ngModelChange,这里日期改变就无法传值给父组件
console.log(date);
this.startDate = date;
this.change(this.auto_off_date);
}
// 日期格式转化
formatDate = function (date) {
var y = date.getFullYear();
var m = date.getMonth() + 1;
m = m < 10 ? '0' + m : m;
var d = date.getDate();
d = d < 10 ? ('0' + d) : d;
return y + '-' + m + '-' + d;
};
// 日历组件初始化
calendarInit() {
this.calendarObj = new CalendarObj();
this.calendarObj.placeholder = '开始日期';
this.calendarObj.readonly = true;
this.calendarObj.yearNavigator = true;
this.calendarObj.monthNavigator = true;
this.calendarObj.showOtherMonths = true;
this.calendarObj.style = { 'width': '93px' };
this.calendarObj.inputStyle = { 'width': '93px', 'background-position': '87px 1px', 'border': '1px solid #c7c7c7' };
}
}
四、form表单-ts代码
formControlName="auto_off_date"接收到数据后,我们需要验证一下数据。 submit(){ console.log(this.goodsFormInfo.value); // 打印表单接收到的数据 if(this.goodsFormInfo.valid){ // 如果通过验证就提交 } } // 表单验证 formInfoInit() { this.goodsFormInfo= this.fb.group({ 'auto_off_date': ['', [this.validDate()]], }) } mesg(field: string) { return this.formUtil.mesg(field); } // 错误提示 private validMessages = { 'auto_off_date': { 'dateError': '请选择定时下架时间' }, }; // 验证 - 定时下架 reqDate() { return ((control) => { console.log(control); if (control.value && control.value.set) { if (control.value.date == '') { return { dateError: false }; } } }); }
(本文原创,转载请注明出处!!)
浙公网安备 33010602011771号