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 }; } } }); }
(本文原创,转载请注明出处!!)