Angular學習筆記(2)——TODO小應用

Angular學習筆記(2)——TODO小應用

1. 寫在前面

之前我們跑了Angular的Hello World,你是不是對它有點感覺了呢?這一篇將結合一個TODO程序來繼續學習Angular的用法。

梳理一下之前的Hello World程序。我們寫了一個main.ts來引導模塊AppModule,而該模塊又包含組件AppComponent,這是一個Angular應用最基本的結構。下面再來簡單地看看Angular各部件的含義。

先看Module(模塊)。Angular應用是模塊化的,每一個Angular應用至少有一個模塊,被稱爲根模塊,通常我們會以AppModule來命名。要想定義一個模塊類,只需要在類上加上@NgModule裝飾器。@NgModule還接收一個元數據對象,該對象有如下幾個重要的屬性,其中前3個在Hello World中已經見到過:

  1. declarations: 聲明本模塊中擁有的視圖類。 Angular 有三種視圖類:組件、指令和管道。
  1. bootstrap: 指定應用的主視圖(稱爲根組件),它是所有其它視圖的宿主。只有根模塊才能設置bootstrap屬性。
  2. imports: 導入其他模板類。
  3. exports: 導出本模塊的視圖類。
  4. providers: 服務的創建者,並加入到全局服務列表中,可用於應用任何部分。

接下來是Component(組件)。組件是負責控制UI上一小塊區域的部件,比如一個用戶列表,一個標題欄等,我們把這一小塊區域稱之爲視圖。要想定義一個組件,我們只需要在組件類的上面添加@Component裝飾器,裝飾器接受的元數據包含視圖的名字(selector),視圖的模板(templatetemplateUrl),視圖的樣式(styleUrls)等屬性。而在組件類裏面,我們會定義一些與視圖相關的屬性和視圖的邏輯操作。

以上的兩個概念ModuleComponent我們在Hello World中都有些體會,在接下來的TODO例子中會介紹模板語法,主要介紹數據綁定,它的意思就是將變量視圖綁定起來,這麼做的好處就是一旦變量發生變化或者視圖發生變化,它們都會“同步更新”對方(雙向數據綁定)。

2. TODO應用

好了,不碼概念了,概念聽得越多越糊塗。還是來看實際的例子比較靠譜。點擊這裏查看演示效果。

這次我們不自己手動建工程了,採用另一種方式:使用git克隆Angular的quickstart項目,在此之上進行開發。

使用如下命令進行克隆:

git clone https://github.com/angular/quickstart  angular-todo

idea或其他IDE打開angular-todo工程,在從命令行輸入npm install安裝依賴庫。接着運行npm start,官方的Hello World就跑起來了,我們幾乎不用修改它寫好的代碼(app.module.ts除外),只需在上面添加代碼即可。

採用Angular模塊化的思維去分析TODO應用,由於功能很簡單,我們沒必要建立新的模塊,只需要建立一個TODO組件即可,頁面分爲如下形式:

下面來實現TodoComponent。首先是新建todo.component.tstodo.component.htmltodo.component.css三個文件。然後在todo.component.ts定義出TodoComponent,並指定模板(通過templateUrl屬性)和樣式(通過styleUrls屬性):

import {Component} from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'todo',
  templateUrl: 'todo.component.html',
  styleUrls: ['todo.component.css']
})
export class TodoComponent {
}

以上和Hello World中的代碼沒有多少區別,相信很容易看懂。Todo組件先寫到這,我們現在將它聲明到AppModule中:

import {NgModule}      from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import {AppComponent}  from './app.component';
import {TodoComponent} from './todo.component';

@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent, TodoComponent],
  bootstrap: [AppComponent]
})
export class AppModule {
}

注意,以上還引入了官方的FormsModule模塊,它用於form元素的相關操作,我們待會會用到,這裏提前引入。現在,我們就可以在AppComponent中使用自定義的todo組件了。和之前分析的結構一樣,模板中先是一個標題,再是todo組件,然後添加點樣式:

import {Component} from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <div class="main-container">
      <h1>TODO</h1>
      <todo></todo>
    </div>
  `,
  styles:[`.main-container{width: 960px; margin: 0 auto;}`]
})
export class AppComponent {
}

現在基本的骨架就OK了,接下來的重點就是todo組件的具體編寫了。之前有提過,使用Angular等數據驅動的框架和寫jQuery等庫最大的不同在於Angular是以數據爲中心,任何變化的視圖(或者不變的視圖,只要你願意)背後必然對應着相應的數據結構。拿我們的todo應用來說,視圖中的todo列表的背後就對應着一個數組,todo列表的每一項就對應數組中的某一個位置上的元素。如果我們想添加或刪除todo列表項,那麼只需要對對應的數組進行添加或刪除操作;如果我們想修改todo列表的某一項的信息,我們只需要修改對應的數組的對應位置上的todo對象即可。要知道,我們用JS修改一個變量,要比直接修改DOM輕鬆的多。如果你是第一次使用Angular這類採用數據驅動的框架,你一定會覺得It's amazing,媽媽再也不用擔心我拼接html字符串了。

有了上面的分析,我們先要將一個todo項的數據結構定義出來,因此在todo.component.ts中,添加如下代碼:

class Item {
  content: string; // todo內容
  date: Date; // todo創建日期
  done: boolean; // 是否完成

  constructor(content: string) {
    this.content = content;
    this.date = new Date();
  }
}

然後在TodoComponent類中,我們需要定義一個Item數組,表示我們的todo列表,並且我們還要需要一個添加todo項的方法:

export class TodoComponent {
  todoList: Item[] = [];

  addTodo(value: any): void {
    this.todoList.push(new Item(value));
  }
}

OK,TodoComponent類暫時就完成了,代碼很簡單,就不做說明了。接下來就是todo組件模板和樣式的編寫。模板代碼其實和我們平常寫的html代碼差不多,不同之處在於加了一些Angular自定義的語法,用於控制頁面的渲染,模板代碼如下:

<p>
  <label for="todo-input">Todo: </label>
  <input id="todo-input" type="text" #input (keyup.enter)="addTodo(#input.value)"/>
</p>
<label>List: </label>
<ol>
  <p *ngIf="todoList.length == 0">Empty...</p>
  <li *ngFor="let item of todoList" [class.done]="item.done">
    <label>
      <input type="checkbox" [(ngModel)]="item.done">
      <span [style.color]="item.done ? 'gray' : 'green'">{{item.content}}</span>
      -
      <span class="hint">{{item.date | date:"yyyy-MM-dd HH:mm:ss"}}</span>
    </label>
  </li>
</ol>

要重點說說上面的模板語法。首先最先讓你困惑的是這句,

<input id="todo-input" type="text" #input (keyup.enter)="addTodo(input.value)"/>

#input是什麼鬼?其實它的官方叫法是template reference variable(模板引用變量),它用於在模板中對DOM元素或指令的引用。這裏#input就是用input變量來引用它所在的input元素(即<input .../>),因此在後面(keyup.enter)="addTodo(input.value)"中,我們可以用input.value拿到輸入框輸入的值。那麼keyup.enter又是什麼鬼呢?它其實被稱爲Event binding(事件綁定)。爲了說清楚事件綁定的概念,我們把它拆開來看,一個是事件,一個是綁定。

先說事件。這和原生js一樣,比如鼠標點擊(click),上面的按鍵後鬆開(keyup)等,都是事件。並且Angular還能自定義事件,這個先不做說明。上面的keyup.enter事件會在鍵盤enter鍵擡起時觸發,觸發後會調用我們在TodoComponent中寫好的addTodo方法,並把input.value作爲參數傳遞進去。

再說綁定。之前已經多次提到過,綁定實際上就是把數據與視圖,事件與方法等關聯起來,一方發生變化或被觸發,另一方自動地隨之做出變化或被觸發。綁定是有方向的,這個方向指的是數據流動的方向,因此可分爲如下三類:

這三類綁定方式的語法分別是[]()[()]

// 第一種,數據源 -> 視圖
<img [src] = "user.avatar">
// 第二種,視圖 -> 數據源
<button (click) = "delete()">刪除</button>
// 第三種,視圖 <-> 數據源
<input [(ngModel)]="user.name">

在todo組件的模板代碼中,我們還用到了兩個指令*ngIf*ngFor,它們是Angular的內置指令,用法根據上面的例子應該就能看懂。最後還有一個語法是管道,操作符是:”|”,這個跟其他框架中所謂的“過濾器”的概念很像。它將操作符左側的表達式的值作爲右側函數的輸入,最終整個表達式的值由右側函數的返回值決定。管道還能串聯。

以上就是模板的基本語法,只是簡單的說明,具體請參考文檔

這樣todo組件的模板就寫好了,最後我們可以在todo.component.css中加些樣式。比如:

.hint{
  color: #999999;
}
.done{
  text-decoration: line-through;
}
li{
  line-height: 2;
}

以上便是整個TODO應用的編寫過程,你跑出來了嗎?目前程序還有一個小缺陷,就是我們回車,輸入的內容被加入todo列表後,輸入框應該被清空,這個就交給你去完成了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章