Angular 2.x折騰記 :(7) 初步瞭解表單:模板驅動及數據驅動及脫坑要點

前言

表單在整個系統中的做用至關重要,這裏主要扯下響應表單的實現方式。css

首先須要操做表單的模塊引入這兩個模塊;html

import { FormsModule, ReactiveFormsModule } from '@angular/forms';typescript

表單控件響應的幾種狀態

模板驅動表單依賴FormsModule,api

數據驅動的表單依賴FormsModule,ReactiveFormsModuleapp

通常作表單校驗及操做推薦用數據驅動的方式,好維護和理解。dom

模板驅動

模板驅動:主要是依賴[(ngModel)]#scope_var以及原生表單控件屬性(require,minlenght,maxlength等)來操做表單的那的值亦或者校驗函數

  • 一個最簡單的例子
<!--#UserName 是局部變量,如果有ngmodel,拿到的就是一個響應對象,如果非ngmodel綁定的,則是dom元素代碼-->
<!--testform這個局部變量保存了表單的全部相關信息-->
<!--ngSubmit是用來觸發表單提交的-->
<!--ngModel相應變量的值-->
<!--$event是原生dom對象-->
<form #testform="ngForm" (ngSubmit)="Submit(testform.value,testform.valid)">
<label for="username">Name</label>
<input type="text" id="username" #UserName="username" class="form-control" required minlength="4" maxlength="24" name="username" [(ngModel)]="username" [ngModelChange]="validate($event)">
<div *ngIf="UserName.valid || (UserName.pristine && !testform.submitted)">
 您輸入的值有誤,請從新輸入
</div>
<button type="submit" >提交</button>
</form>

複製代碼

有兩種方式處理來對上面的表單作校驗;工具

  1. Submit()函數內,在點擊提交的時候對整個表單一一去判斷,傳統方式基本這樣
  2. 每一個控件輸入的時候對應去觸發對應的事件作校驗,好比[ngModelChange]來處理雙向綁定的值校驗

數據驅動(Reactive Form)

響應式表表單:原理是一開始就構建整個表單,表單的值經過特殊指令formControlName一一關聯(相似ngModel);flex

相關名詞動畫

  • FormGroup: 用來追蹤表單控件有效狀態及值 =》 能夠理解爲獲取且能夠操做整個表單的數據
  • FormBuilder:表單數據構建工具[構建初始表單],簡化構建代碼(包括了new FormGroup(),new FormControl(),new FormArray()),FormGroup()內置多種校驗方式
  • formControlName: 同步與FormGroup構建表單內相同字段的值!

項目中的案例

html

<div [@flyIn]="true">
  <div class="beautify-form" *ngIf="!showLoading">
    <div class="page-header">
      歡迎登陸
    </div>
    <form [formGroup]="form" (ngSubmit)="onSubmit(form)">
      <div class="form-group" [ngClass]="{ 'has-danger': form.controls.UserName.invalid && form.controls.UserName.value ,'has-success': form.controls.UserName.valid && form.controls.UserName.value }">
        <div class="input-group input-group-lg">
          <span class="input-group-addon fpd fpd-ordinarylogin1"></span>
          <input type="text" class="form-control" formControlName="UserName" placeholder="手機號碼 \ 郵箱 ">
        </div>
        <div class="form-control-feedback" *ngIf="(form.controls.UserName.dirty || form.controls.UserName.pristine) && form.controls.UserName.invalid && form.controls.UserName.value">帳號不符合規範</div>
        <div class="form-control-feedback" *ngIf="(form.controls.UserName.dirty || form.controls.UserName.pristine) && form.controls.UserName.valid && form.controls.UserName.value">帳號符合規範</div>
      </div>
      <div class="form-group" [ngClass]="{ 'has-danger': form.controls.PassWord.invalid && form.controls.PassWord.value ,'has-success': form.controls.PassWord.valid && form.controls.PassWord.value }">
        <div class="input-group input-group-lg">
          <span class="input-group-addon fpd fpd-mima"></span>
          <input type="PassWord" class="form-control" formControlName="PassWord" placeholder="請輸入密碼">
        </div>
        <div class="form-control-feedback" *ngIf="(form.controls.PassWord.dirty || form.controls.PassWord.pristine) && form.controls.PassWord.invalid && form.controls.PassWord.value ">密碼不符合規範,請從新輸入</div>
        <div class="form-control-feedback" *ngIf="(form.controls.PassWord.dirty || form.controls.PassWord.pristine) && form.controls.PassWord.valid && form.controls.PassWord.value ">密碼符合規範</div>
      </div>
      <div class="form-group ">
        <div class="flex">
          <div class="beautify-wrap flex-wrap">
            <input type="checkbox" class="beautify-checkbox" name="rememberme" id="rememberAccount" formControlName="rememberAccount">
            <label for="rememberAccount"></label>記住帳號
          </div>
          <!--<a [routerLink]="['/account/reset-pw']">忘記密碼</a>-->
        </div>
      </div>
      <div class="message-tips" *ngIf="messageTips">
        <i class="fpd fpd-error"></i> {{messageTips}}
      </div>
      <div class="form-group ">
        <button class="btn btn-lg btn-outline-success btn-block" type="submit" [disabled]="form.invalid">登陸</button>
      </div>
      <div class="form-group">
        <span class="noaccount-notify">沒有帳號?點擊</span><a [routerLink]="['/account/collect']" class="collect-user">用戶登記</a>
      </div>
    </form>
  </div>
  <div class="loading" *ngIf="showLoading">
    <app-mit-loading [option]="'load4'"></app-mit-loading>
  </div>
</div>


複製代碼

component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms'; // 引入表單的一些特性
import { Router } from '@angular/router';
import { AccountService } from '../../services/account.service';
import { environment } from '../../../../../environments/environment';
import { flyIn } from '../../../../animation/flyIn';
import { Observable } from 'rxjs/Observable';
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
  animations: [flyIn]
})
export class LoginComponent implements OnInit, OnDestroy {
  public form: FormGroup; // 表單對象
  public showLoading = false;
  public messageTips: string;
  public login_subscribe: any;
  
  
  // Validators的寫法注意事項
  // v2.x版本這樣的寫法是可行的,v4有調整,否則不會生效
  // 'UserName':'', [ Validators.compose([Validators.minLength(6)] 
  
  // v4+ , 第一位的''表明這個元素初始化構建爲空值,相似未輸入狀態
  // 'UserName': ['', Validators.compose([Validators.minLength(6)]
  
  // Validators可選參數
  // 1. required :必須驗證的,返回布爾值
  // 2. minLength : 最小長度
  // 3. maxLenght: 最大長度
  // 4. nullValidator : 空值判斷
  // 5. coompose :多重判斷組合,下面有寫法 
  // 6. pattern是支持正則模式,正則謹記轉義轉義轉義
  constructor(private fb: FormBuilder, private router: Router, private account: AccountService) {
    this.form = fb.group({
      'UserName': ['', Validators.compose([Validators.minLength(6) || Validators.pattern('(0|86|17951)?(-)?1[3,4,5,7,8,9]\\d{9}') || Validators.pattern('[\\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+')])],
      'PassWord': ['', Validators.compose([Validators.required, Validators.pattern('\\w{8,16}')])],
      'rememberAccount': ['']
    });
  }


  ngOnInit() {

  }

  // 登陸事件
  onSubmit(e) {

    this.showLoading = true;
    this.login_subscribe = this.account.login(e.value).subscribe((res) => {
     console.log('省略。。。。。。')
    }, (err) => {
      this.showLoading = false;
    });
  }

  ngOnDestroy() {
    if (this.login_subscribe) {
      this.login_subscribe.unsubscribe();
    }
  }

}


複製代碼

效果圖

嵌套表單

有些時候咱們接口數據層次不可能只有一層,出現兩層三層都有可能,這時候須要咱們構建一個嵌套表單。。。

html

  • v2-的寫法:表單的取值能夠用controls直接點出來
<div class="custom-card">
  <div class="custom-card-body">
    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
      <div class="row" formGroupName="RuleContent">
        <div class="col-sm-12 col-md-12 col-lg-8 offset-lg-2">
          <div class="form-group row" [ngClass]="{ 'has-danger': form.controls.RuleContent.controls.FenceName.invalid && form.controls.RuleContent.controls.FenceName.value ,'has-success': form.controls.RuleContent.controls.FenceName.valid && form.controls.RuleContent.controls.FenceName.value }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度柵欄名稱</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="text" class="form-control" formControlName="FenceName" placeholder="柵欄名稱">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              不超過十個字
            </div>
          </div>
          <div class="form-group row" [ngClass]="{ 'has-danger': form.controls.RuleContent.controls.MaxSpeed.invalid && form.controls.RuleContent.controls.MaxSpeed.value ,'has-success': form.controls.RuleContent.controls.MaxSpeed.valid && form.controls.RuleContent.controls.MaxSpeed.value }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度閾值</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="number" class="form-control" min="1" formControlName="MaxSpeed" placeholder="整數">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              km/h
            </div>
          </div>
          <div class="form-group row">
            <div class="col-12 col-sm-10 col-md-6 offset-sm-2 offset-md-4 offset-lg-3">
              <button type="submit" class="btn btn-primary" [disabled]="form.invalid">保存</button>
              <button type="button" class="btn btn-secondary" (click)="back()">取消</button>
            </div>
          </div>
        </div>
      </div>
    </form>

  </div>
</div>


複製代碼
  • v4+的寫法 :嵌套表單的取值必須用.get()來獲取,否則會報錯誤,具體緣由是api改動了,看下官方文檔就知道,改動了挺多(不單單這塊)
<div class="custom-card">
  <div class="custom-card-body">
    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
      <div class="row" formGroupName="RuleContent">
        <div class="col-sm-12 col-md-12 col-lg-8 offset-lg-2">
          <div class="form-group row" [ngClass]="{ 'has-danger': form.get('RuleContent.FenceName').invalid && form.get('RuleContent.FenceName').value ,'has-success': form.get('RuleContent.FenceName').valid && form.get('RuleContent.FenceName').value }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度柵欄名稱</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="text" class="form-control" formControlName="FenceName" placeholder="柵欄名稱">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              不超過十個字
            </div>
          </div>
          <div class="form-group row" [ngClass]="{ 'has-danger': form.get('RuleContent.MaxSpeed').invalid && form.get('RuleContent.MaxSpeed').value ,'has-success': form.get('RuleContent.MaxSpeed').valid && form.get('RuleContent.MaxSpeed').value }">
            <label tooltip="" class="col-sm-10 col-md-3 form-control-label col-lg-3 star">速度閾值</label>
            <div class="col-sm-8 col-md-6 col-lg-6">
              <input type="number" class="form-control" min="1" formControlName="MaxSpeed" placeholder="整數">
            </div>
            <div class="col-2 col-sm-4 col-lg-3 flex-align-center">
              km/h
            </div>
          </div>
          <div class="form-group row">
            <div class="col-12 col-sm-10 col-md-6 offset-sm-2 offset-md-4 offset-lg-3">
              <button type="submit" class="btn btn-primary" [disabled]="form.invalid">保存</button>
              <button type="button" class="btn btn-secondary" (click)="back()">取消</button>
            </div>
          </div>
        </div>
      </div>
    </form>

  </div>
</div>


複製代碼

components.ts

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; // 引入表單的一些特性

// 動畫
import { fadeIn } from '../../../../../animation/fadeIn';

// 服務
import { SpeedFenceService } from '../speed-fence.service';
import { EventsService } from '../../../../../services/events-service.service';


@Component({
  selector: 'app-modify',
  templateUrl: './modify.component.html',
  styleUrls: ['./modify.component.scss'],
  animations: [fadeIn]
})
export class ModifyComponent implements OnInit {
  public form: FormGroup;
  public getId: any;
  public id: number;
  constructor( private speedFenceService: SpeedFenceService, private eventsService: EventsService, private router: Router, private activatedRoute: ActivatedRoute, private fb: FormBuilder ) {
    this.form = fb.group({
      'ID': 0,
      'RuleContent': this.fb.group({
        'MaxSpeed': [0, Validators.compose([Validators.required, Validators.pattern('(([4-9][0-9])|(1[0-1][0-9])|(120))')])],
        'FenceName': ['', Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(10)])],
      })

    });
  }

  ngOnInit() {
    this.checkAction();
    // console.log(this.form);
  }

  // 獲取ID
  checkAction() {
    this.activatedRoute.params.subscribe((params: { id: string }) => {
      console.log(params);
      if (params.id) {
        console.log(this.id);
        this.id = parseInt(params.id, 10);
        this.form.controls['ID'].setValue(this.id);
        this.GetSpeedFenceSettingByFenceId({ FenceId: parseInt(params.id, 10) });
      }
    });
  }

  GetSpeedFenceSettingByFenceId(data) {
    this.speedFenceService.GetSpeedFenceSettingByFenceId(data).subscribe(
      res => {
        if (res.State) {
          this.form.controls['RuleContent'].setValue({
            'MaxSpeed': res.Data.RuleContent.MaxSpeed || '',
            'FenceName': res.Data.RuleContent.FenceName || '',
          });
        }
      },
      err => { }
    );
  }

  onSubmit(form) {
    console.log('此處省略。。。。。');

  }


  // 取消
  back() {
    if (this.id) {
      this.router.navigate(['../../'], { relativeTo: this.activatedRoute });
    } else {
      this.router.navigate(['../'], { relativeTo: this.activatedRoute });
    }
  }



}

複製代碼

總結

多看手冊多動手,纔是真理。

有不足之處或者錯誤之處請留言指出,會及時跟進修正,謝謝閱讀

相關文章
相關標籤/搜索