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