close

把玩了AngularJS一個月,試作了CRUD這些基本的功能,其實也不難,只是得先搞懂service、directive、factory、scope...這些物件的原理、用法。

我先以bootstrap拉了一個主版面出來,如下圖所示:

擷取1  

View的區塊就是讓template透過Route指引Multi Views來自動變化。而我想在一開始時先表列資料,點選某筆資料後,畫面切換到資料的明細,亦即List-Detail的呈現方式。

List畫面 :

擷取2

 

Detail畫面 :

 擷取3  

在檔案架構上,我以功能為導向,命名了一個Phone的資料夾,建立3個js檔:services.js、directives.js、controllers.js。

萬一哪邊有問題,能很快速的找到該檔案中的哪一支程式需要檢查。

而在版面上,我建構了一支主版(Phone.aspx),以及兩支樣版(PhoneList.html、PhoneDetail.html)。在主板上,最重要的是要加入一個<div ng-view></div>的區塊,讓樣板能從該區塊被載入,例如:

<%@Page Language="C#"AutoEventWireup="true"CodeFile="Phone.aspx.cs"Inherits="Phone"%>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" ng-app="Phone"  >

<head runat="server">

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

    <title></title>

</head>

<body>

    <form id="form1"runat="server">

           <divclass="row-fluid text-center banner">

               <h1>Master Page - Header</h1>

           </div>

            <div  ng-view></div>

            <div class="navbar footer">

                <div class="row-fluid text-center"><h1>Master Page - Footer</h1></div>

            </div>

        </div>

    </form>

</body>

</html>

 


 

而在樣板檔案中,你則可以恣意的以<div></div>區塊搭配Bootstrap的css類別宣告來設計版面。例如PhoneList.html:

 

<style type="text/css">

     .hover{background-color:lightgray}

</style>

<div ng-controller="PhoneListCtrl">

    <div class="row-fluid">

        <button type="button" class="btn btn-primary  pull-left" ng-click="AddPhone()">Add List</button>

    </div>

    <div class="row-fluid"  ng-repeat="phone in phones" ng-mouseover="RowHightlight($index)" ng-class="{hover : selectedRow==$index}">

        <div class="span2"style="margin-top:15px; padding:5px;"><img ng-src="{{phone.imageUrl}}"   /></div>

        <div class="span8 offset1"  >

                <div class="row success">

                    <class="icon-star-empty"></i>

                    <b><href="#/detail/{{phone.id}}">{{phone.name}}</a></b>

                </div><br/>

                <div class="row" style="text-align:left">

                    <span> {{phone.snippet}}</span>

                    <button class="btn btn-primary  pull-right" ng-click="DeletePhone(phone)">Delete</button>

                </div>

         <hr/>

        </div>

    </div>

</div>

 


js的部分,首先看services.js

var appMoudle = angular.module('Phone', ['ngResource']);

appMoudle.factory('ServicePhone', ['$resource', function ($resource) {

    return $resource('/api/Phone/:id', { id: '@id' }, {

        query: { method: 'GET', isArray: true }, //取得phones列表(Read)

        get: {method:'GET'},                     //取得phone物件(Read)

        save: { method: 'POST'},                   //新增(Create)

        update: {method:'PUT'},                  //更新(Update)

        remove: {method:'DELETE'}                //刪除(Delete)

    });

}]);

由於我有調用ngResource這個Service來幫我處理資料往返前後端,所以必須把它加入陣列中。記得將angular-resource.js參考拉到主版檔案中。然後利用了工廠方法定義了名為ServicePhone服務,並且在此物件中定義了四個方法:savequeryget、update、remove來對應C、R、U、D。爾後這個服務將用來注入controller中。

接下來controllers.js:

 

appMoudle.config(['$routeProvider', function ($routeProvider) { //定義路由表

    $routeProvider.

        when('/', {

            templateUrl: '/View/PhoneList.html',

            controller: 'PhoneListCtrl'

        }).

        when('/detail/:id', {

            templateUrl: '/View/PhoneDetail.html',

            controller: 'PhoneDetailCtrl'

        }).

        otherwise({ redirectTo: '/' });

}]);

首先透過註冊一個底層$routeProvider,來配置路由。如果Path為/結尾,則ng-View中則導入PhoneList.html樣板,並且為該樣版配置PhoneListCtrl Controller;要是Path包含/detail/id編號,則導入PhoneDetail.html樣板,並為該樣版配置PhoneDetailCtrl Controller。

以下則定義了兩個Controller:PhoneListCtrl、PhoneDetailCtrl,並且為Controller注入先前自行定義的ServicePhone服務,而利用該服務調用\修改\刪除資料時,語法為

ServicePhone.query()

ServicePhone.get({id:$route.current.params.id}):

 

ServicePhone.save({}, phone)

ServicePhone.update({}, phone)

是不是很簡短易懂^^

 

 

appMoudle.controller({

    'PhoneListCtrl': ['$scope', 'ServicePhone', function ($scope, ServicePhone) {

        $scope.phones =   ServicePhone.query();  

        $scope.RowHightlight = function (row) { //滑鼠移過時,背景色hightlight

            $scope.selectedRow = row;

        }

        $scope.DeletePhone = function (phone) { //刪除某筆資料

            var index = jQuery.inArray(phone, $scope.phones);

            //勿用ngResourcedeletedeleteIE下是關鍵字,使用會有問題,改用remove

            ServicePhone.remove({ id: index });

            $scope.phones.splice(index, 1); //刪除phones中的某一筆資料

        }

        $scope.AddPhone = function () { //新增一筆資料

            var phone = {};

            phone.age = "0";

            phone.id = $scope.phones.length + 1;

            phone.imageUrl = "img/phones/motorola-xoom-with-wi-fi.0.jpg";

            phone.name = "Motorola XOOM\u2122 with Wi-Fi";

            phone.snippet = "The Next; Next Generation\r\n\r\nExperience the future with Motorola XOOM \

                        with Wi-Fi; the world's first tablet powered by Android 3.0 (Honeycomb).";

            phone.price = 12000

            ServicePhone.save({}, phone, function (phone) { //success callback

                $scope.phones.push(phone);

                alert('Add Phone Success!');

            })

        }

    }],

    'PhoneDetailCtrl': ['$scope', 'ServicePhone','$route', function ($scope, ServicePhone,$route) {

        var phone = ServicePhone.get({id:$route.current.params.id}); //取得id後,傳回後端調用資料

        $scope.phone = phone;

        $scope.isShow = true;

        $scope.EditPhone = function (phone) { //更新資料

            ServicePhone.update({}, phone, function (phone) {

                alert('Edit Phone Success!');

            });

        }

    }]

});

 

另外我在PhoneList.html樣板上,加了滑鼠移到列表時highlight的功能,先在樣板上定義ng-mouseover directive,例如:ng-mouseover="RowHightlight($index)"RowHightlight是定義在PhoneDetailController中的方法,$index則是ng-repeat directive內建的屬性,回傳值為列表的index值。另外還提供了其他的屬性($first、$middle、$last....),請參閱官網http://docs.angularjs.org/api/ng.directive:ngRepeat。簡單來說,我將列表的index值傳給PhoneDetailController中的selectedRow model,再利用ng-class上定義的表達式ng-class="{hover : selectedRow==$index},如果selectedRow的值等於$index值,則觸發hover 這個css類別,而hover就會執行其所定義的css語法.hover{background-color:lightgray}。

而在PhoneDetail.html樣板上,為了製作編輯的效果,加了將div置換為textarea的功能。這部分則把它寫成了directive,並存在directives.js :

 

appMoudle.directive('editable', ['$compile', function ($compile) {

    return function (scope, element, attrs) {

        var template, elmt;

        element.bind('click', function () {

            if (scope.isShow) {

                elmt = element.prev('.snippet')

                template = angular.element('<textarea class="snippet" rows="5" ng-model="phone.snippet" cols="100"></textarea>');

            } else {

                elmt = element.prev().prev('.snippet');

                template = angular.element('<span class="snippet"> {{phone.snippet}} </span>');

                scope.EditPhone(scope.phone);

            }

            scope.isShow = !(scope.isShow);

            $(elmt).html($compile(template)(scope));

            scope.$apply();

        })

    }

}])

 

 

然後把directive放到<div>中,例如 : 

<divclass="row"style="text-align:left; margin-left:5px;">

    <span class="snippet"> {{phone.snippet}}</span>

    <span class="pointer" editable ng-show="isShow"><i  class="icon-pencil"></i></span>

    <span class="pointer" editable ng-hide="isShow"><iclass="icon-ok"></i></span>

</div>

利用isShow model的值傳回editable directive來控制切換鉛筆及打勾的圖示,且因為該directive放在PhoneDetailController中,所以透過editable 中的scope,能拿到在PhoneDetailController中所定義好的EditPhone方法,來執行編輯後的保存動作。在editable中也能利用jQuery指令來找到你要的元素,去do something後,再利用$compile()(scope)重新compiler DOM,再呼叫$apply檢查model內的值重新binding。

擷取  

 

 擷取2  


而在後端,我是用MVC4所提供的Web Api,原因是他是利用通訊協定中的GET、POST、PUT、DELETE來對應程式中的method,而AngularJS的ngResource也是,兩個剛好一搭一唱,前端直接丟物件給後端,而後端方法的參數同樣也定義為物件型別,只要兩邊的屬性一致,後端接資料完全沒問題!這太方便了,要是屬性一多,以往在web Service裡就得定義一堆的參數,web api似乎能自動轉換(懶的去證明了:P)。詳細的web api使用方法,請參閱以下連結:http://www.dotblogs.com.tw/ian/archive/2013/05/27/104900.aspx 。文中的大大是以vs2012去建立web api,但事實上vs2010也行,不過要安裝MVC4後,才有選項能選。MVC4下載路徑:http://www.microsoft.com/zh-tw/download/details.aspx?id=30683

擷取3  

 

這個CRUD的實作,其實花了我一個多禮拜才寫完,中間遇到不明之處,便網上四處爬文找資料,在摸索過程中,亦深感Angularjs設計者的巧思,讓程式具有了"美感",雖然它不像jQuery那麼"精緻",但它明確的定義了"責任"的邊界,使程式碼更具可讀性以及複用性。

範例下載

arrow
arrow
    全站熱搜

    忙裡偷閒 發表在 痞客邦 留言(2) 人氣()