iT邦幫忙

7

邊學AngularJS邊做Todo List (4) - 修改待辦事項

我們的todo list已經有了新增刪除的功能,還差一步修改,就算五臟俱全了。

這次新加入的修改功能,會使用到ng-showng-hideng-dblclick這幾個AngularJS的新指令。

另外,AngularJS官方剛發佈他們在Google也啟用了CDN,因此我們也將angular.min.js從原本的官網提供的連結,改指向CDN的路徑。

AngularJS CDN路徑:

https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js

也建議大家可以改用AngularJS CDN。

這次的修改的檔名我們命名為「todoWithUpdate.html」,而Controller則和上回一樣,新增「TodoCrtlUpdate」這個controller,是在「TodoCtrlRemovable」基礎上加入本回的功能,以便對照不同之處。要記得修改HTML的controller對應。這些後面就不再說明。
Todo List的修改功能我們會這樣設計,在待辦事項上面雙擊(double click),待辦事項會變成可編輯的狀態,編輯完後按下儲存,修改便完成。

【兩行HTML做到編輯修改功能】

我們先來看這次的完整HTML碼:

<html ng-app>
  
    <meta charset="utf-8">  
    <title>邊學AngularJS邊做Todo List (4)</title>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
    <script type="text/javascript" src="js/controllers.js"></script>
  
  <body ng-controller="TodoCrtlUpdate">
      <h1>Todo List</h1>
      <form ng-submit="addItem()">
        <input type="text" ng-model="newItem" name="newItem" />
        <input type="submit" id="submit" value="新增待辦事項" />
      </form>
      <ul id="todo">
        <li ng-repeat="item in todoList | filter:{isFinish:false}">
          <div ng-hide="item.editing"><input type="checkbox" ng-click="removeItem(item)"><span ng-dblclick="edit(item)">{{item.label }}</span></div>
          <div ng-show="item.editing"><input type="text" value="{{item.label }}" ng-model="item.label"><button ng-click="save(item)">儲存</button></div>
        </li>
      </ul>

      <hr>

      <h1>Finished!</h1>  
      <ul id="finish">
        <li ng-repeat="item in todoList | filter:{isFinish:true}">
          {{item.label}}
        </li>
      </ul>
  

完工後的Layout與功能會是這樣:

修改的地方主要有兩處,我們這次要談的新概念,也都在這裡:

  <div ng-hide="item.editing"><input type="checkbox" ng-click="removeItem(item)"><span ng-dblclick="edit(item)">{{item.label }}</span></div>


  <div ng-show="item.editing"><input type="text" value="{{item.label }}" ng-model="item.label"><button ng-click="save(item)">儲存</button></div

首先我們看到上次刪除的功能,包在一個有新指令ng-hide的div元素中。另外下方一行,則是為了修改而新增的功能,同樣有一個新指令ng-show,另外則是處理編輯時的dom。

我們先談ng-show/ng-hide這個一看就知道是成對的指令,它的作用是在符合某運算式時,讓元素出現就消失。

這兩個指令同樣指向item的屬性editing,當editing為真時,第一行會藏起來,第二行出現;反過來說,當editing的結果為假時,第一行則會出現,第二行則為隱藏,由於item本來沒有這個editing的值,所以預設的外觀就是這一個。

【貼近AngularJS思維來設計】

這裡要特別說明的是,AngularJS預設是使用jQuery去操控DOM,它會去偵測網站有沒有使用jQuery,有的話當然就直接呼叫,如果沒有,則會使用框架中內建的jQuery Lite。也就是說,你完全可以用jQuery來操作DOM。

在設計這個修改功能時,我第一直覺的想法是用jQuery來取得原本的待辦事項,然後在空中生成編輯所需要的DOM(以我們這個例子來說就是一個input和一個button),當然還要加上事件監聽來處理使用者按下「儲存」的動作,最後把資料寫進todo List,把DOM復原成原來的樣子。

想了一圈之後,覺得真麻煩,麻煩的東西,就不會是AngularJS的風格了。

怎麼用AngularJS的思維來設計修改功能,讓外觀反應資料,資料更新外觀這樣的平滑的連動呢?

合理的想法是,進入編輯模式時,原本的待辦事項的DOM藏起來,出現的是可編輯的待辦事項。編輯模式結束後,兩個的顯示狀態再交換回來。因此只要有一個編輯模式的狀態,其餘的事就可以通通交給AngularJS了。

也因此,我找到了ng-show和ng-hide這兩個指令,讓它們同時都指向item.editing這個屬性,利用item.editing的有無,做到兩個元素顯示狀態切換,這樣就有AngularJS的味道了。

順著這樣的思維,我們需要的就是需要觸發這種狀態的時機:
(1)進入編輯模式:利用ng-dblclick指令,在double click的時後,用”edit(item)”來進入編輯模式。
(2)結束編輯模式:利用按下儲存按鈕時,ng-click觸發”save(item)”,來結束編輯模式。

切換動作作完,最後只剩下資料更新的部分。用過去的處理dom的思維,是要把更新編輯後的內容,塞回去原來的todo list陣列中的物件,這裡面少不了用迴圈去比對,找出剛剛更新的是哪個物件,再把新的內容塞進label中。

但是AngularJS不是這樣做事的,記住它雙向綁定資料的優異功能,我們只要在編輯框加上ng-model=”item.label”之後,它就知道這文字框裡面的資料是和item的label綁定的,這裡所作的異動,都會即時地更新。因此我們只要這樣指定,就完全不用去處理資料的部分。

【透過edit和save函式來切換狀態】

這次的controller我們只需要新增edit和save兩個函式:

 $scope.edit = function(item){
      item.editing = true;
  }

  $scope.save = function(item){
    delete item.editing;
  }

原理非常簡單,edit函式其實是為item加上editing的屬值,並給它true的值,一旦賦值之後,因為資料狀態異動,外觀的ng-show、ng-hide馬上會被觸發,於是原本的待辦事項就會隱藏,而待辦事項編輯框就會出現。

save函式作的則是相反的動作,編輯完成後,我們不需要editing這個多餘的屬性來干擾原來的資料結構,於是就用javascript的delete的語法,刪去item物件的editing屬性。這樣一來ng-show和ng-hide會再偵測到資料異動,於是狀態又會回復成原來的狀態。

就這樣,不用寫多少的code,不用直接去處理dom的變化,我們又再次完成了任務。

【AngularJS的心法】

到這裡,我們已經完成todo list的新增、修改、刪除的功能了。如果大家有跟著走一次,一定不難發現AngularJS的強大威力與其帶來的便利性。

事實上我自己也是邊寫邊摸索,寫這幾回也獲得了一些小小心得,就是寫AngularJS要換掉寫JQuery的腦袋。

過去JQuery優異的DOM處理功能,讓我們可以很直覺地去把DOM拿來拿去,資料也會順著這著的流程塞來塞去。好處是直觀,但壞處是code可能會有點dirty,久了不好維護,更別說如果HTML有調整時,JS往往就得重寫了。

這也是隨著Web App盛行之後,JavaScript也有各種MVC、MVVM、MVP...設計概念出爐,讓應用程式可以發展的更穩健。

我學得還不深,但是從目前的學習心得來說,應用AngularJS時,時時刻刻要記住的就是資料與外觀之間的表裡關係,資料是裡、外觀是表,外觀就是資料狀態的反應,而外觀在和使用者異動後的改變,也會回過頭來異動資料。這個基本原則記住,應該會對學習AngularJS有幫助。

【番外篇】

本來在設計修改功能時,我想要用的是edit in place的設計,一樣是雙擊之後變成編輯狀態,而編輯完後,按下enter或是離開文字框後就自動復原。

後來沒有這樣設計有兩個原因,一個是比起現在的程式會復雜一點,較不能聚焦在AngularJS本身上,另一個是離開文字框自動復原需要監聽blur事件,但是API中沒有ng-blur這個指令。

雖然後來在http://angular-ui.github.com/index.html 這個非官方的網站上找到支援,想要用也是可以用,但是因為一樣會偏離AngularJS的應用,所以就放棄了。

這個事件也告訴我們,AngularJS雖然版本已經是1.0.1了,不過也還沒到盡善盡美的地步。

Live Demo ::完整的程式碼


邊學AngularJS邊做Todo List (1) - Hello World
邊學AngularJS邊做Todo List (2) - Todo List 動起來
邊學AngularJS邊做Todo List (3) - Todo List 刪項目
邊學AngularJS邊做Todo List (4) - 修改待辦事項
邊學AngularJS邊做Todo List (5) - 為測試作準備
邊學AngularJS邊做Todo List (6) - E2E測試(上)


2 則留言

0
ted99tw
iT邦高手 1 級 ‧ 2012-07-21 18:41:46

先推再說~~灑花

0
zbrong
iT邦新手 5 級 ‧ 2013-04-24 17:12:27

妙!临时添加一个属性editing来设置修改状态,用完后去掉。

我要留言

立即登入留言