AngularJS實戰項目(Ⅰ)--含源碼

前言


鑽研ABP框架的日子,遇到了不少新的知識,由於對本身而言是新知識,因此常常卡在不少地方,遲遲不能有所突破,做爲一個稍有上進心的程序員,心裏絕對是不服輸的,也絕對是不畏困難的,心底必然有這樣一股力量「I must conquer it!」。好比,之前沒用過AutoMapper,那我就去學,最後將學到的寫成了博客,和你們一塊兒分享,爭取讓那些還沒接觸的人少走彎路。如今,在前端設計時,ABP不少用到的都是AngularJS,而以前作項目都是用的JQuery,因此繼續研究ABP也是至關有阻力。可是,不能由於有阻力就拖延,最終致使半途而廢吧!於是,仍是得學,畢竟技多不壓身嘛!何況,JQuery是MPA【經典】,而AngularJS是SPA【年輕】,這兩種框架都掌握了,也算是挺圓滿的吧。javascript

如今回到正題,這篇博客,咱們將會看到如何使用AngularJS建立Web應用。對初學者而言,涵蓋並解釋一些AngularJS框架的基本特徵。我設計了一個簡單的關於產品的CRUD的例子,解釋了演示中全部的代碼片斷。css

背景


當前,AngularJS做爲Javascript的MVC(也有人說是MV*,暫且不糾結這個)框架被普遍使用,它爲更快且更容易地開發響應式的Web提供了強大的機制。做爲MVC框架,它將Web前端代碼分紅三個組件Model,View和Controller。所以,在data model,應用邏輯(Controllers)和view展現之間有明確的分離,讓你更容易地關注關鍵的開發區域。view接收來自model的數據來展現。當用戶經過點擊或者敲擊鍵盤和應用交互時,controller經過改變模型中的數據進行響應。最終,view獲得了發生在model中的變化這個通知,從而它能更新展現的內容。html

在Angular應用中,view是DOM(文檔對象模型),controller是javascript類,model數據存儲在對象屬性中。AngularJS將集成在客戶端的html和數據傳輸給瀏覽器。就像Jquery庫同樣,爲了使用新鮮的model狀態更新UI,你必須和DOM交互。例如,不管你什麼時候觀察到model中的任何改變,你都必須和DOM交互來影響這個改變(若是之前常用Jquery,那麼這句話不難理解)。然而,AngularJS提供了數據綁定,咱們沒必要將數據從一個地方移到另外一個地方,只須要使用javascript屬性映射UI部件,而後它就會自動同步了。前端

使用代碼


源代碼下載

如今開始寫代碼,讓咱們一步一步地建立這個簡單的CRUD操做案例。java

添加一個index.html頁面,並添加一個屬性ng-app屬性,例如:程序員

<html ng-app="demoApp">

這句代碼就將咱們的應用定義爲AngularJS應用。由於AngularJS是SPA(單頁面應用)框架,因此它會使用html視圖模板做爲分部視圖來響應肯定的路由。爲了渲染一個分部視圖,咱們會給一個div標籤添加一個ng-view屬性,全部的分部視圖都會渲染在這個div標籤中。angularjs

<div ng-view=""></div>

下載兩個文件angular.jsangular-route.js,在index.html中添加這些文件的引用。或者你也能夠用CDN的靜態資源,好比百度CDN的靜態資源http://apps.bdimg.com/libs/angular.js/1.4.6/angular.js數據庫

<script src="../Scripts/angular.min.js"></script>
<script src="../Scripts/angular-route.min.js"></script>

分部視圖


如今來點咱們CRUD案例的真實的內容,咱們會開發兩個分部視圖,ProductList.html和ProductEdit.html。先來看一下ProductList.html的代碼:api

<div class="container">
    <h2 class="page-title">產品列表</h2>

    <div class="searchbar">
        <ul class="entity-tabular-fields">
            <li>
                <label>搜索:</label>
                <span class="field-control">
                    <input type="text" ng-model="filter.productName" value=" " />
                </span>
                <label></label>
            </li>
        </ul>
    </div>
    
    <h2><a href="#/ProductEdit">新增產品</a></h2>
    
    <table class="items-listing">
        <thead>
            <tr>
                <th>代碼</th>
                <th>名稱</th>
                <th>描述</th>
                <th>類別</th>
                <th>操做</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="product in products|filter:filter.productName"> 
                <td><a href="#/ProductEdit?code={{product.code}}">{{product.code}}</a></td>
                <td>{{product.name}}</td>
                <td>{{product.description}}</td>
                <td>{{product.category}}</td>
                <td><a href="#/?code={{product.code}}">刪除</a></td>
            </tr>
        </tbody>
    </table>
</div>

效果圖:數組

image

除了html和css,更要關注angular指令的使用。在<tbody>中,咱們使用了指令ng-repeat="product in products | filter:filter.productName"。products是一個javascript數組,它是存儲在內存中的。該指令告訴javascript循環遍歷數組中的每一個元素(若是你使用過C#的話,那麼它就至關於C#中的foreach),而且爲每一個元素生成一個<tr>標籤。接下來,咱們添加了過濾器,該過濾器根據productName過濾出咱們搜索條件中要求的一些元素,也許正如你看到的,它是一個對象屬性,該屬性經過數據綁定指令ng-model="filter.productName"綁定在搜索框上。所以不管你在搜索框中輸入什麼,產品數組都會使用匹配的字符串過濾。爲了輸出一些值到html上,咱們使用angular的數據綁定語法{{output_Expression}},例如表格單元的產品屬性:

<td>{{product.name}}</td>

對於第一個單元格,咱們顯示了product.code,它會連接到第二個分部視圖ProductEdit.html

最後一個單元格也包含了一個到列表展現頁面的連接,它會使用產品代碼product.code做爲查詢字符串。後面咱們會看到models/controllers的Javascript代碼。如今看一下ProductEdit.html代碼:

<div class="container">
    <h2 class="page-title">產品編輯</h2>
    <br/>
    
    <ul class="entity-tabular-fields">
        <li class="entity-field-row">
            <label>產品代碼:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.code"/>
                <label></label>
            </span>
        </li>
        <li class="entity-field-row">
            <label>產品名稱:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.name" />
                <label></label>
            </span>
        </li>
        <li class="entity-field-row">
            <label>產品描述:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.description"/>
            </span>
            <label></label>
        </li>
        <li class="entity-field-row">
            <label>產品種類:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.category"/>
            </span>
            <label></label>
        </li>
        <li class="entity-field-row">
            <span class="field-control">
                <button ng-click="saveProduct()">保存</button>
            </span>
        </li>
    </ul>
</div>

效果圖:

image

注意咱們給全部的input標籤加了ng-model屬性,這個將model(product)屬性和相應的UI元素綁定在一塊兒。在這個案例中,會自動地建立一個新的叫作「currentProduct」的產品對象,而且使用用戶在文本框中輸入的值生成該對象的屬性。最後,在button標籤中添加了一個ng-click的屬性,該屬性綁定了按鈕的點擊事件saveProduct,該事件在當前的$scope中。這個$scope,你能夠把它認爲一個特殊的Javascript對象,咱們當前的視圖全部必須的對象或者方法都綁定在$scope上,咱們能夠從視圖中訪問在$scope中聲明的任何東西。隨着咱們繼續推動model或者controller代碼,你會更清楚的。

Javascript/Angular 部分


如今,該看看Javascript部分的代碼了。以前已經在index.html中添加了兩個AngularJS庫的引用,如今再添加一個Javascript文件,咱們命名爲「App.js」,並將該文件的引用添加到index頁面中。

首先,經過調用angular.module聲明咱們的應用對象。

var demoApp = angular.module('demoApp', ['ngRoute']);

第一個參數「demoApp」是咱們定義的模塊名稱,第二參數是咱們的模塊必需要有的依賴數組,當前博客的案例中,咱們只依賴「ngRoute」

如今咱們爲分部視圖配置路由:

//配置路由
demoApp.config(function($routeProvider) { $routeProvider .when('/', { controller: "ProductController", templateUrl: "partials/ProductList.html" }) .when('/ProductEdit', { controller: "ProductController", templateUrl: "partials/ProductEdit.html" }) .otherwise({ redirectTo: '/' }); });

參數$routeProvider傳到路由配置的函數中,而後使用了一個鏈式的when函數來尋找url路徑,而且傳遞一個對象來定義和controller相聯繫的目標視圖。Controller屬性定義了在templateUrl屬性中指定的分部視圖會使用哪個控制器。咱們的例子中只使用了一個控制器ProductControllerotherwise()函數的做用是,若是沒有指定的路徑匹配到請求的url路徑,就會匹配的默認視圖。能夠聯想一下switch…case…default語法。

是時候定義ProductController了,在AngularJS中,有多種方式定義控制器。我麼如今使用一個controllers對象,而後把全部的controller加到這個對象中,最後將這個對象配置給咱們app模塊的controllers屬性。

每個controller都必須傳遞一個$scope參數,視圖要使用它來訪問data model中的數據。在$scope中定義的任何數據均可以在視圖中直接訪問。若是須要的話也可使用一些其餘可選的參數$route, $routeParams, $location

在這個控制器中,咱們添加了和ProductFactory交互的支持函數,經過添加到$scope對象中使得這些函數在視圖中可使用。咱們用到了一個事件「$viewContentLoaded」,這個事件在分部視圖加載到div標籤中時觸發。由於咱們對於添加和編輯任務都是用的ProductEdit頁面,所以我將被編輯的產品的code做爲查詢字符串參數。而後在這個事件中檢測查詢參數code,若是有此參數,視圖就是編輯模式,不然就是一個建立新產品的頁面。刪除也是相似的原理,若是咱們在查詢字符串參數中檢測到了code,那麼就刪除產品,而後刷新列表。

你可能會注意到控制器中的另外一個參數ProductFactory,這是咱們須要從控制器訪問數據的服務組件。Angular框架容許用不一樣的方式建立服務組件。最經常使用的一種方式是使用工廠建立。咱們須要在應用模塊中添加該服務,該應用模塊包含兩個參數:服務名稱和工廠函數。別懼怕,它不過是一個返回新對象的簡單函數。

  1 //定義controllers對象
  2 var controllers = {};
  3 
  4 controllers.ProductController= function($scope,$route,$routeParams,$location,ProductFactory) {
  5     $scope.products = [];
  6 
  7     var init= function() {
  8         $scope.products = ProductFactory.getProducts();
  9     }
 10 
 11     var initProductEdit= function() {
 12         var code = $routeParams.code;
 13         if (code==undefined) {
 14             $scope.currentProduct = {};
 15         } else {
 16             $scope.currentProduct = ProductFactory.loadProductByCode(code);
 17         }
 18     }
 19 
 20     $scope.$on('$viewContentLoaded', function() {
 21         var tempalteUrl = $route.current.templateUrl;
 22         if (tempalteUrl=="partials/ProductEdit.html") {
 23             initProductEdit();
 24         }else if (tempalteUrl=="partials/ProductList.html") {//大小寫要和temlateUrl中的大小寫保持一致
 25             var code = $routeParams.code;
 26             if (code!=undefined) {
 27                 $scope.deleteProduct(code);
 28             }
 29         }
 30     });
 31 
 32     init();
 33 
 34     $scope.saveProduct= function() {
 35         ProductFactory.saveProduct($scope.currentProduct);
 36         $location.search('code', null);
 37         $location.path('/');
 38     }
 39 
 40     $scope.deleteProduct= function(code) {
 41         ProductFactory.deleteProduct(code);
 42         $location.search('code', null);
 43         $location.path('/');
 44     }
 45 }
 46 
 47 //將全部的控制器賦值給app模塊
 48 demoApp.controller(controllers);
 49 
 50 //定義工廠
 51 demoApp.factory('ProductFactory', function () {
 52     //初始化產品數組
 53     var products = [
 54     {code:'P001',name:'Lumia 950XL',description:'win10系統最好的手機,帶有黑科技色彩',category:'mobile'},
 55     {code:'P002',name:'Lumia 950',description:'win10系統次好的手機,相比XL低個檔次',category:'mobile'},
 56     {code:'P003',name:'Surface Pro Book',description:'微軟最具創新的筆記本',category:'Notebook'},
 57     {code:'P004',name:'Surface Pro 4',description:'微軟最好的PC/平板二合一產品',category:'Surface'},
 58     { code: 'P005', name: 'Surface 4', description: '微軟次好的PC/平板二合一產品', category: 'Surface' },
 59     {code:'P006',name:'Surface Phone',description:'傳說中微軟下一代win10系統超旗艦手機',category:'mobile'}
 60     ];
 61 
 62     var factory = {};
 63     factory.getProducts= function() {
 64         return products;
 65     }
 66 
 67     factory.loadProductByCode= function(code) {
 68         var productFound={};
 69         for (var i = 0; i < products.length; i++) {
 70             if (products[i].code==code) {
 71                 productFound = products[i];
 72                 break;
 73             }
 74         }
 75         return productFound;
 76     }
 77 
 78     factory.saveProduct= function(product) {
 79         var tempProduct = factory.loadProductByCode(product.code);
 80 
 81         if (tempProduct == null || tempProduct == undefined) {
 82             tempProduct = {};
 83             tempProduct.code = product.code;
 84             tempProduct.name = product.name;
 85             tempProduct.description = product.description;
 86             tempProduct.category = product.category;
 87         } else{
 88             
 89             tempProduct.code = product.code;
 90             tempProduct.name = product.name;
 91             tempProduct.description = product.description;
 92             tempProduct.category = product.category;
 93 
 94             products.push(tempProduct);
 95         }
 96     }
 97 
 98     factory.deleteProduct= function(code) {
 99         var tempProduct = factory.loadProductByCode(code);
100 
101         if (tempProduct!=null) {
102             products.remove(tempProduct);
103         }
104     }
105     return factory;
106 });
點擊查看代碼

 

如上面的代碼,咱們使用模塊的工廠函數來定義工廠服務組件。代碼很明顯,它只包含了一些幫助屬性和方法。代碼末尾的deleteProduct函數使用了一個數組的remove函數,它不是默認的數組函數,我定義了一個函數,把它添加到Array.prototype中,使得它能夠在網站中的任何地方均可以訪問。但要確保在應用加載的開始處理這個,在一些相關的代碼執行以前執行,好比我放到了index.html頁面中的script標籤中。

在工廠組件中,咱們常常必須從server中存取數據,但在這篇博客中,咱們只用到了臨時的內存中的產品數組。在下一篇博客中,我將從server的數據庫中存取數據。

註解:

$route:用於控制器和視圖(html分部視圖)的深度連接。查看更多。

$routeParams:該服務容許檢索當前路由參數的集合。查看更多。

$location:該服務分析瀏覽器地址欄中的URL(基於window.location),使得該URL對應用可用。查看更多。

 

源代碼下載

相關文章
相關標籤/搜索