1. 程式人生 > >Angular 2 + 折騰記 :(7) 初步瞭解表單:模板驅動及資料驅動及脫坑要點

Angular 2 + 折騰記 :(7) 初步瞭解表單:模板驅動及資料驅動及脫坑要點

前言

表單在整個系統中的作用相當重要,這裡主要扯下響應表單的實現方式。
首先需要操作表單的模組引入這兩個模組;
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

表單控制元件響應的幾種狀態

模板驅動表單依賴FormsModule,資料驅動的表單依賴FormsModule,ReactiveFormsModule
一般做表單校驗及操作推薦用資料驅動的方式,好維護和理解。。

模板驅動

模板驅動:主要是依賴[(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);

相關名詞
- 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_-][email protected][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 });
    }
  }



}

總結

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

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

相關推薦

Angular 2 + 折騰 (7) 初步瞭解模板驅動資料驅動要點

前言 表單在整個系統中的作用相當重要,這裡主要扯下響應表單的實現方式。 首先需要操作表單的模組引入這兩個模組; import { FormsModule, ReactiveFormsModule } from '@angular/forms';

Angular 2 + 折騰 (1)初識Angular-cli[官方腳手架]要點

前言   這個系列的進度有些跳躍性,我儘量直白點解釋,但是我不是官方文件,直入主題!!!! 什麼是Angular-cli 簡言之:就是NG團隊自行維護的一個`腳手架`[內建單元測試及

HTML+CSS專案課2利用table和製作“網易郵箱註冊頁面”

知識點:html文件基本結構、table標籤佈局、表單標籤的使用、img標籤、a標籤、p標籤等常見標籤的使用。 製作網頁效果:   網頁製作思路: 1、將整個網頁分成4部分:3個表格+底部段落文字(3個表格設定同樣的寬度,水平居中,邊框為0等屬性) 2、表格1

Retrofit 2使用要點梳理淺析POST檔案/上傳

實習期的第一個任務就是為專案組預研FACE++智慧人臉識別這一新功能。呼叫曠視FACE++人臉識別介面,進行人臉識別有兩種方式:一是通過先上傳圖片到雲端儲存網站(網盤,雲盤,七牛雲等)獲得圖片檔案對應的URL引數,通過圖片的網路URL引數呼叫FACE++介面;二是在手機客戶端直接上傳檔案呼叫FACE++介

js控制 按enter進行搜索或提交

cti search onkeydown var 點擊 ear win doc all js控制 按enter進行搜索或提交表單://按enter 進行搜索document.onkeydown = function(e){ var ev = document.all

activiti自己定義流程之整合(四)整合自己定義部署流程定義

borde row ont 創建 source als dst art select 綜合前幾篇博文內容。我想在整合這一部分中應該會有非常多模塊會跳過不講,就如自己定義表單的表單列表那一塊,由於這些模塊在整合的過程中都差點兒沒有什麽修改,再多講也是反復無用功。

第二章模板

實際應用 新版 page rms 模板語言 file erl style 模式 在第一章中,我們學習了使用Tornado創建一個Web應用的基礎知識。包括處理函數、HTTP方法以及Tornado框架的總體結構。在這章中,我們將學習一些你在創建Web應用時經常會用到的更強大的

讀書筆記HTML祕籍Web

//一個html5的表單 <!DOCTYPE html> <html>  <head>   <meta charset="utf-8" />   <title></title&

VUE事件處理和輸入繫結

事件處理 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>07_事件處理</title> </

VUE事件處理和輸入綁定

點擊 event http def nbsp mode 愛好 gpa method 事件處理 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"

Atitit 面試技術點最小化問題法總結 目錄 1. Web 前端 1 1.1. Jq 常用操作哪些?? 1 1.2. 查詢後如何繫結後端資料到表格 2 1.3. 提交後怎麼接受資料 2 2.

Atitit 面試技術點最小化問題法總結     目錄 1. Web 前端 1 1.1. Jq 常用操作哪些?? 1 1.2. 查詢後如何繫結後端資料到表格 2 1.3. 提交後怎麼接受表單資料 2 2. Mvc Springmvc 2 2.1

JSP-實驗傳值-提交並傳遞、獲取資料

JSP-實驗:表單傳值-提交表單並傳遞、獲取資料 說明: 參考連結在最後1 我這個版本執行過,反正沒大問題,但是用參考的那個網頁上的內容,就會出錯……原因也寫在後面了。 實驗 實驗內容: 利用表單傳遞資料,此題目包含01.html、01.jsp

JSP-詳細總結formname= method=post(get) action=

(學習語言:JSP) 文章目錄 name="form" method=post (或get) get方法 post方法 對比【表格】 `action="___.jsp"`

使用O2OA二次開發搭建企業辦公平臺(十三)流程開發篇報銷審批流程開發

本部落格為O2OA系列教程、O2OA使用手冊,教程目錄和各章節天梯將在連載完後更新。 使用O2OA二次開發搭建企業辦公平臺(一)平臺部署篇:平臺下載和部署 使用O2OA二次開發搭建企業辦公平臺(二)平臺部署篇:埠衝突和伺服器埠配置 使用O2OA二次開發搭建企業辦公平臺(三)平臺部署篇:使用外部資料庫

element UI驗證一個複雜迴圈渲染後資料修改後的部分單項驗證

 一、前言 普通的表單驗證參考element UI官方文件即可找到解決方案並順利進行程式碼實現,官方也給出了幾個示例,是很好的參考。不過,對於複雜的表單驗證,官方文件並沒有給出過多示例或者說明。文章中的例項就是在實際專案中遇到的一個複雜表單的驗證問題。 文章中前端程式碼基

20181022mysql操作一建立庫,的增刪改查,資料的增刪改

1、建立資料庫 create database python charset=utf8; 2、使用資料庫 use python; 3、建立表結構 create table student(     id int primary key auto_increment

PHPcurl模擬form上傳檔案

<form action="" method="post" enctype="multipart/form-data"> <input type="file" name="upload"> <buttion>submit</button> </f

【HTML&CSS】HTML實現互動

瀏覽器通過HTML表單和伺服器實現互動,表單是一個可供使用者輸入資訊的頁面,使用者提交表單後,表單包含的資訊會發送到一個Web伺服器,由伺服器指令碼負責處理並響應,返回一個HTML頁面,最後瀏覽器顯示這個頁面。 表單元素 <form> 所有表單

[Swift通天遁地]二、表格-(2)建立右側帶有索引的UITableView(檢視)

本文將演示如何給表格新增索引功能。 在專案導航區,開啟檢視控制器的程式碼檔案【ViewController.swift】 現在開始編寫程式碼,建立一個表格,並在表格右側新增一列快捷索引。 1 import UIKit 2 3 //使當前的檢視控制器類,遵循表格的資料來源協議UIT

XHTML/CSS 網頁5種簡單技術

技術1:標籤三明治 將輸入框,選擇框和文字框全部包含進 label 元素,並全部設定為塊級元素。將單選按鈕和多選框顯示方式設定為 inline 以便於它們在同一行出現。如果你比較喜歡 label 和單選按鈕/多選框出現在不同行,可以選擇不把它包含在 label 裡面,或者使用硬換行處理。