1. 程式人生 > >Angular2學習筆記.4、表單相關,雙向資料繫結,HeroForm

Angular2學習筆記.4、表單相關,雙向資料繫結,HeroForm

開始

本次我們將會學會如何用Angular建立表單、two-way data binding(雙向資料繫結)、change tracking(檢測變化)、validation(驗證) 和 error handling(錯誤處理)等功能以及ngModel、ngControl和ngForm等指令的應用。
這次內容比較長,在最後會給出一個完成的工程來實踐本篇的內容。在這個過程中你可以配合演示來實時地觀察效果。
參考官方文件

工程結構

/  
|-package.json  
|-index.html  
|-node_modules/  
  |-libraries  
  |-...  
|-HeroForm/ |-index.html |-app/ |-main.js |-app.component.js |-hero.js |-hero-form.component.js |-template/ |-hero-form.component.template |-css/ |-styles.css

正文

概覽

表單創造了了一種有cohesive(粘合力)、effective(有效)並且compelling(引人入勝)的資料輸入體驗(來自官方文件,我不能很好的翻譯和解釋這句話)。一個Angular表單用來協調進行了資料繫結的使用者控制元件、track(檢測)改變、驗證輸入和顯示錯誤。
我們都使用過表單進行過登入、提交幫助請求、下訂單、訂飛機、設定會議日程和其他無數的資料輸入任務。表單支撐起了商業應用。
任何經驗豐富web開發者都可以用正確的標籤來拼湊HTML表單。更具挑戰的是通過表單背後的資料流來建立有粘合力的資料輸入體驗以幫助使用者的行為變得有效且高效。
在表單這部分,Angular處理了很多重複、模式化的任務,因此我們不必浪費很多精力。
我們將要建立一個表單,用於輸入一個hero的相關資訊,它有name、power(能力)、alter ego(密友)這三個屬性,其中name、power是必填的,alter ego是可選的,power是一個列表以選擇其中一項。當用戶沒有填寫name時,會給出驗證錯誤,提示name是必填項。當沒有正確填寫時,不可點選提交按鈕。點選提交後會顯示出剛剛提交的資訊,此時點選編輯會重新填寫表單。

建立hero的Model

建立表單,首先需要一個數據模型提供基礎。在使用者輸入表單資料時我們會捕捉到改變並且更新到Model的一個例項。
這個Model只包含了hero的三個屬性。
建立/HeroForm/app/hero.js,內容為:

(function(app) {
  app.Hero = Hero;
  function Hero(id, name, power, alterEgo) {
    this.id = id;
    this.name = name;
    this.power = power;
    this.alterEgo = alterEgo;
} })
(window.app || (window.app = {}));

這段程式碼非常簡單,不在贅述。

表單元件

一個Angular表單包含兩部分:基於HTML的模板和基於程式碼的元件來處理資料和使用者互動(Model對Angular表單來說不是必須的)。
接下來建立一個元件來協調View和Model。
建立/HeroForm/app/hero-form.component.js,內容為:

(function(app) {
  app.HeroFormComponent = ng.core.Component({
      selector: 'hero-form',
      templateUrl: '/HeroForm/app/template/hero-form.component.template'
    }).Class({
      constructor: function() {
        this.powers = ['Really Smart', 'Super Flexible',
          'Super Hot', 'Weather Changer'
        ];
        this.model = new app.Hero(18, 'Dr IQ', this.powers[0],'Chuck Overstreet');
        this.submitted = false;
      },
      onSubmit: function() {
        this.submitted = true;
      },
      diagnostic: function() {
        return JSON.stringify(this.model);
      }
  })
})(window.app || (window.app={}));

可以看到定義了一個HeroFormComponent元件到window.app,這個元件選擇了模板中的<hero-form>標籤來進行例項化,模板通過URL定位。在建構函式中初始化了power陣列,定義了hero的各種能力供使用者選擇。這在之前的文章都已經提到過了。在建構函式中還例項化了一個Hero作為model成員,這個成員會和表單進行互動。submitted表示表單是否被提交過了,控制表單的顯示。diagnostic用於除錯,讓我們直觀地看到model的變化。

建立一個HTML模板初始化表單

建立/HeroForm/app/template/hero-form.component.template,內容為:

<div class="container">
  <div [hidden]="submitted">
    <h1>Hero Form</h1>
    <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
      {{diagnostic()}}
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" required [(ngModel)]="model.name" ngControl="name"  #name="ngForm" >
        <div [hidden]="name.valid" class="alert alert-danger">
          Name is required
        </div>
      </div>
      <div class="form-group">
        <label for="alterEgo">Alter Ego</label>
        <input type="text" class="form-control" [(ngModel)]="model.alterEgo" ngControl="alterEgo" >
      </div>
      <div class="form-group">
        <label for="power">Hero Power</label>
        <select class="form-control" required [(ngModel)]="model.power" ngControl="power" #power="ngForm" >
          <option *ngFor="#p of powers" [value]="p">{{p}}</option>
        </select>
        <div [hidden]="power.valid" class="alert alert-danger">
          Power is required
        </div>
      </div>
      <button type="submit" class="btn btn-default" [disabled]="!heroForm.form.valid">Submit</button>
    </form>
  </div>
  <div [hidden]="!submitted">
    <h2>You submitted the following:</h2>
    <div class="row">
      <div class="col-xs-3">Name</div>
      <div class="col-xs-9  pull-left">{{ model.name }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">Alter Ego</div>
      <div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">Power</div>
      <div class="col-xs-9 pull-left">{{ model.power }}</div>
    </div>
    <br>
    <button class="btn btn-default" (click)="submitted=false">Edit</button>
  </div>
</div>
基礎內容

Name的<input>標籤和Power的<select>標籤包含了HTML5的required屬性以表示它們是必須填寫的。
class中的大部分樣式(除了ng-valid與ng-invalid)均來自Twitter Bootstrap,這些都是為了美化。Angular不需要任何Class或Style的外部庫,你可以使用任何庫或根本不用。
先看看Power的選單,這裡用到了之間文章中說到的,通過ngFor指令遍歷陣列,對每個元素產生<option>#p迭代了powers中的而每一個元素,在{{p}}中顯示出來。
最後的<button class="btn btn-default" (click)="submitted=false">Edit</button>會在按鈕被點選時執行submitted=false,重啟編輯狀態。

雙向資料繫結

ngModel指令使我們簡單的將表單繫結到Model。
看看Name的<input>標籤,你會看到[(ngModel)]="model.name"的用法。[()]表示這裡使用了雙向繫結。在繫結中,資料從Model流向View使用[],資料從View流向Model用()。因此雙向的資料繫結使用[()],也就是說你在表單上的更改會更新到Model,修改Model也會同步顯示在表單中。
你也可以將它改為:

<input type="text" class="form-control" required [ngModel]="model.name" (ngModelChange)="model.name = $event" ngControl="name"  #name="ngForm" >

ngModelChange不是<input>元素的事件,它時間上是ngModel指令的一個event property(事件屬性)。當Angular看到一個繫結標記如[(x)],它認為指令x有一個叫做x的輸入指令和一個叫做xChange的輸出指令。
對於model.name = $event。ngModelChange不會產生DOM事件。這是一個Angular EventEmitter屬性,當它被fire(啟用)時返回了輸入值,這正是Model的name屬性所需要的。

ngForm
<select class="form-control" required [(ngModel)]="model.power" #power="ngForm" >

Angular將ngForm暗中新增進了<form>中。這裡設定了一個模板本地變數power並使用ngForm為他賦值。我個人認為這裡使用的ngForm指代了當前它所在的控制元件。
ngForm指令為<form>添加了一些附加的特性。它收集表單中的被標記ngControl(後面會提到)標記了的元素並監測他們的屬性(包括是否可用)。它也有自己的valid屬性,當每一個包含的control為true時它會為true。
因此<form (ngSubmit)="onSubmit()" #heroForm="ngForm"><button type="submit" class="btn btn-default" [disabled]="!heroForm.form.valid">Submit</button>配合,模板本地變數heroFrom代表了整個表單,通過整個表單的valid值判斷是否可以提交。

ngControl

表單不僅僅是資料繫結,我們同樣需要知道表單上的控制元件的狀態。ngControl指令持續檢測控制元件的狀態。ngControl和ngForm類似,只能用於<form>中的元素。我們的應用可以通過ngControl知道使用者是否觸摸了控制元件,值是否改變了以及內容是否值變得無效了等。

<input type="text" class="form-control" required [(ngModel)]="model.name" ngControl="name"  #name="ngForm" >

我們用值ngForm初始化模板本地變數name,Angular會將name設定為ngControl指令的例項。或者說,對於這個<input>,name handle(我喜歡翻譯為“作為..的控制代碼”)了這個ngControl物件。
ngControl不僅僅檢測狀態,它使用特殊的Angular CSS classes來更新控制元件。我們可以通過改變class的值來改變控制元件的外觀並且使訊息顯示或者消失。

解釋 為true時的Class 為false時的Class
控制元件被訪問了 ng-touched ng-untouched
控制元件值被改變了 ng-dirty ng-pristine
控制元件值是可用的 ng-valid ng-invalid

表單中控制元件的class內容會隨著控制元件內容的變化而改變。本工程會用到ng-valid和ng-invalid,因為我們需要強烈的視覺資訊來告訴使用者哪些值是必填的(非空值是ng-valid,空值是ng-invalid),後面我們會寫這部分CSS。

通過ngSubmit提交表單

使用者在完成填寫後需要提交表單。Submit按鈕本身不會做任何事情,但它會觸發一個表單的提交(type="submit")。我們需要為<form>新增ngSubmit指令,並且通過一個event繫結將它繫結到HeroFormComponent.submit()方法上。

其他

HTML最後一部分的<div [hidden]="!submitted">將submitted做使用者hidden,控制元素的可見性。

CSS

新增CSS以支援上文中說的ng-valid和ng-invalid樣式。
新增/HeroForm/app/css/styles.css,內容為:

.ng-valid[required] {
  border-left: 5px solid #42A948; /* green */
}
.ng-invalid {
  border-left: 5px solid #a94442; /* red */
}

關於CSS的內容不再贅述。

其他部分

工程還有一些檔案作為必要的模組如引導等,我將它們集中起來描述。

根元件

建立/HeroForm/app/app.component.js,內容為:

(function(app) {
    app.AppComponent = ng.core.Component({
        selector: 'my-app',
        template: '<hero-form></hero-form>',
        directives: [app.HeroFormComponent]
    }).Class({
        constructor: function() {}
    });
})(window.app || (window.app = {}));

這段程式碼很熟悉了,唯一不同的是將app.HeroFormComponent作為指令使用,這個根元件使用了HeroFormComponent,它會被正確使用。

引導

建立/HeroForm/app/main.js,內容為:

(function(app){
    document.addEventListener('DOMContentLoaded',function(){
        ng.platform.browser.bootstrap(app.AppComponent);
    });
})(window.app || (window.app = {}));

通過根元件來引導應用。

index.html

建立/HeroForm/index.html,內容為:

<html>
  <head>
    <title>HeroForm</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 1. Load libraries -->
    <!-- IE required polyfill -->
    <script src="/node_modules/es6-shim/es6-shim.min.js"></script>
    <script src="/node_modules/angular2/bundles/angular2-polyfills.js"></script>
    <script src="/node_modules/rxjs/bundles/Rx.umd.js"></script>
    <script src="/node_modules/angular2/bundles/angular2-all.umd.js"></script>
    <link rel="stylesheet" href="/HeroForm/app/css/styles.css">
    <!--引用了www.bootcss.com中的bootstrap樣式,這樣做可以減少讀者在實踐過程中出錯的可能性,因為在第一篇筆記的基礎結構中沒有引入bootstrap。-->
    <link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. Load our 'modules' -->
    <script src='/HeroForm/app/hero.js'></script>
    <script src='/HeroForm/app/hero-form.component.js'></script>
    <script src='/HeroForm/app/app.component.js'></script>
    <script src='/HeroForm/app/main.js'></script>
  </head>
  <!-- 3. Display the application -->
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

正像註釋中所說,我在第一篇筆記中沒有引入bootstrap,為了減少大家出錯的可能性,我直接引用了www.bootcss.com的bootstrap,在此表示感謝。

執行

在/下執行npm start

相關推薦

Angular2學習筆記.4相關雙向資料HeroForm

開始 本次我們將會學會如何用Angular建立表單、two-way data binding(雙向資料繫結)、change tracking(檢測變化)、validation(驗證) 和 error handling(錯誤處理)等功能以及ngModel、ngC

vue.js v-model雙向資料 vue.js form資料

vue.js v-model雙向資料繫結, vue.js form表單資料繫結   ================================ ©Copyright 蕃薯耀 2018年11月29日 http://fanshuyao.iteye.com/   &l

Vue學習(3)————————ClassStyle,雙向資料dom節點

標籤內繫結屬性(此功能看來可以動態繫結標籤屬性) <template> <div id="app"> <div v-bind:title="title"> 滑鼠走一走 </div> </div> </temp

4.新增相關的屬性

ext require orm nodelist node ogr 位置 data 以及 新增表單相關的屬性   1、可以對input(type=text)、select、textarea與button元素指定autofocus屬性。它以指定屬性的方式讓元素在打開頁面時自動

HTML基本標籤相關標籤

HTML基本標籤 標題標籤及水平線: 標題標籤 <h1></h1>~<h6></h6>標籤在HTML頁面中建立標題,標題預設加粗,換行。 水平線 在 HTML 頁面中建立一條水平分隔線,用於定義內容中的主題變化

Angular2學習筆記.3通過模板呼叫Controller事件實現使用者輸入和顯示UserInput

通過模板呼叫Controller事件實現使用者輸入和顯示 本篇的內容會比較少,但是遵循著官方教程的步驟,還是單獨發出來吧。這個工程實現了將使用者在<input>中的輸入傳入到Controller的相應事件,並把它在也變其他地方顯示出來。 參考官方

《React-Native系列》4介面程式碼編寫

今天就開始投入到RN專案了,做後端出生,寫Android和iOS確實有點勉強,不過還是要把這段經歷記錄下來。 要實現的功能介面如下: 這個是最簡單的Form表單,第一步實現介面吧,不管點選效果,不管篩選控制元件...  思路: 我把這幾類控制元件分為了3類: 1、帶頭

Angular2學習筆記.1環境部署基本概念HelloAngular2

前言 作為本系列筆記的第一篇文章,在開頭要說一些閒言碎語。 首先我是一個Angular2的初學者,這個系列文章不是教程或其他一切厲害的東西,僅僅是作為我學習過程的一個記錄,我在編輯和排版後將它作為資料儲存並分享給其他人。也希望大家和我共同以學習的態度探討技術

TQ2440 學習筆記—— 4安裝 Ubuntu 下的開發工具

使用的板子是TQ2440,但學習的視訊是韋東山老師的視訊,所以虛擬機器裡面用的Linux是韋東山老師介紹的Ubuntu 9.10 下面是在Ubuntu9.10裡面安裝開發工具 由於第一次使用Ubuntu,對命令的使用不是特別熟悉,所以記錄下。 先將光盤裡Linux目錄下

STL學習筆記--4序列式容器之list

1、概述 list:地址不連續的空間。每次插入或刪除一個元素,就配置或釋放一個元素空間。對於任意位置的元素插入或刪除,list永遠是常數時間。 2、list節點 list本身和list節點是不同的; template <class T>

STL學習筆記--4序列式容器之vector

常見的資料結構:array陣列,list連結串列,tree樹,stack棧,queue佇列,hash table散列表,set集合,map對映…… 根據資料在容器中的排列分為:序列式sequence和關聯式associative。 序列式容器之vector

Angular2學習筆記.2ngFor和ngIf指令MVVM分離DisplayingData

本篇概述 本片為Anguar2學習筆記的第二篇文章,將會講述Angular2的資料繫結功能(僅涉及單向),ngFor、ngIf指令的使用以及如果分離MVVM。 本片內容參照官方文件DisplayingData。 工程演示 工程結構 這次我們來建

shiro學習筆記 過濾器 shiro 驗證碼 登入

自己自定義實現了一個驗證碼錶單過濾器,基於FormAuthenticationFilter 程式碼如下: package cn.ddsxy.ddlx.shiro; import cn.ddsxy.ddlx.util.CaptchaUtil; import org.apac

Vue.js實現雙向資料自動賦值自動取值)

1、使用Vue.js實現雙向表單資料繫結,例子 <!--html程式碼--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta

Element 驗證項v-model值為物件下的子物件的屬性時無法驗證

問題:表單項(select)雙向繫結的變數(form.a.name)為表單物件(form)的子物件(form.a)的某一屬性時(form.a.name),規則繫結的prop(name)驗證不執行。 <el-form :model="form" :rules="rules" ref="f

【Vue.js學習筆記】5:雙向資料,計算屬性

雙向資料繫結 雙向資料繫結往往會用到input、select、textarea等表單標籤上,因為總是涉及一個數據資料的地方和輸出資料的地方。 當資料發生變化的時候,檢視也就發生變化,當檢視發生變化的時候,資料也會跟著同步變化。 資料雙向繫結,一定是對於UI控制元件來說的,

JS學習筆記——AngularJS 1.x雙向資料機制

0.前言 AngularJS和vueJS是前端比較熱門的兩個框架,AngularJS 1.x是我第一個接觸的框架,雙向繫結是其最大的特點,我們從原生JS的角度看看,這個雙向資料繫結是如何實現的。點這裡看vueJS的雙向繫結原理。 1.簡單的雙向繫結實現

Vue 框架-03-鍵盤事件健值修飾符雙向資料

Vue 框架-03-鍵盤時間及健值修飾符 一、鍵盤事件,當按鍵盤時,在控制檯輸出提示 html 原始碼: <!DOCTYPE html> <html> <head> <meta charset="utf-8" />

Vue學習(三)——屬性雙向資料

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>屬性繫結和雙向資料繫結</title> <script src="./v

vue 雙向資料的實現學習(二)- 監聽器的實現

廢話:上一篇https://www.cnblogs.com/adouwt/p/9928278.html  提到了vue實現的基本實現原理:Object.defineProperty() -資料劫持 和 釋出訂閱者模式(觀察者),下面講的就是資料劫持在程式碼中的具體實現。 1.先看如何呼