走進AngularJs(一)angular基本概念的認識與實戰

來源:互聯網
上載者:User

標籤:web應用   反饋   review   執行   變數賦值   back   推斷   是什麼   擴充   

一、前言

  前端技術的發展是如此之快,各種優秀技術、優秀架構的出現簡直讓人目不暇接,作為一名業界新秀,緊跟時代潮流,學習掌握新知識自然是不敢怠慢。當聽到AngularJs這個名字並知道是google在維護它時,便一直在關注,看到其在國外已經十分火熱,可是國內的使用方式卻有不小的差距,參考文獻/網路文章也很匱乏。朝思暮想良久,決定深入學習angular,並寫系列部落格,一方面作為自己學習路程上的記錄,另一方面也給有興趣的同學一些參考。

  首先我自己是一名學習者,會以學習者的角度來整理我的行文思路,故該系列部落格也不能叫做教程,只能是些探索,有理解或是技術上的錯誤還請大家指出。其次我特別喜歡編寫小例子來把一件事情說明白,故在文中會儘可能多的用樣本加代碼講解,我相信這會是一鐘比較好的方式。最後,我深知在現有條件下對於angular的學習會困難重重,不過我更相信堅持的力量,所以謹以此文作為日後學習的動力,讓我們一起來走進angular的世界吧~

二、AngularJs是什麼

  這個定義一定要定準了,AngularJs(後面就簡稱ng了)是一個用於設計動態web應用的結構架構。首先,它是一個架構,不是類庫,是像backbone一樣提供一整套方案用於設計web應用。它不僅僅是一個javascript架構,因為它的核心其實是對HTML標籤的增強,有圖有真相,請看官網描述:

 

  何為HTML標籤增強?其實就是使你能夠用標籤完成一部分頁面邏輯,具體方式就是通過自訂標籤、自訂屬性等,這些HTML原生沒有的標籤/屬性在ng中有一個名字:指令(directive)。後面會詳細介紹。那麼,什麼又是動態web應用呢?與傳統web系統相區別,web應用能為使用者提供豐富的操作,能夠隨使用者操作不斷更新視圖而不進行url跳轉。ng官方也聲明它更適用於開發CRUD應用,即資料操作比較多的應用,而非是遊戲或影像處理類應用。

  為了實現這些,ng引入了一些非常棒的特性,包括模板機制、資料繫結、模組、指令、依賴注入、路由。通過資料與模板的綁定,能夠讓我們擺脫繁瑣的DOM操作,而將注意力集中在商務邏輯上。這些我將在以後的學習中一一研究。

  另外一個疑問,ng是MVC架構嗎?還是MVVM架構?官網有提到ng的設計採用了MVC的基本思想,而又不完全是MVC,因為在書寫代碼時我們確實是在用ng-controller這個指令(起碼從名字上看,是MVC吧),但這個controller處理的業務基本上都是與view進行互動,這麼看來又很接近MVVM。讓我們把目光移到官網那個非醒目的title上:“AngularJS — Superheroic JavaScript MVW Framework”。

  好吧,MVW。W—whatever。隨便是MV什麼好了,所以也有人寫為了MV*。其實糾結這個也真沒必要,等今後對整個架構熟悉了,其中結構自然瞭然於心。

三、開始運行angular

  有了一個大概的朦朧的瞭解就夠了,我相信很多概念在使用的過程中會慢慢清晰。我迫不及待的想要讓angular運行起來了。動手~

  首先從官網http://angularjs.org/下載angular.js,引入你的頁面中,然後我們使用最簡單的手工啟動方式,直接調用bootstrap方法。所有的代碼如下:

<!DOCTYPE html><html ><head><meta charset="utf-8" />  <title>運行ng</title><script type="text/javascript" src="../angular.js"></script></head><body><div>    1+1={{1+1}}</div>   <script>angular.bootstrap(document,[]);</script></body></html>

  只有一行代碼。調用bootstrap方法傳入範圍和初始化的模組數組(此處為空白)。是不是很簡單。你很快會看到一處比較特別的,就是這裡:

<div>    1+1={{1+1}}</div>

  如果你使用過其他模板庫,應該對這種寫法不陌生了,{{}}雙大括弧,這是ng的模板中用於書寫運算式的標記,ng成功運行起來後,{{}}內的運算式會生效,即頁面會顯示如下:

 

  為了不讓你把ng看的這麼簡單,我必須告訴你一般是不這麼啟動的,來看稍微修改以後的代碼:

<!DOCTYPE html><html ng-app="MyApp"><head><meta charset="utf-8" />  <title>運行ng</title><script type="text/javascript" src="../angular.js"></script></head><body><div>    1+1={{1+1}}</div>   <script type="text/javascript" charset="utf-8">var app = angular.module(‘MyApp‘, [], function(){console.log(‘started‘)});</script></body></html>

  在<html>標籤上多了一個屬性ng-app=”MyApp”,它的作用就是用來指定ng的範圍是在<html>標籤以內部分。在js中,我們調用angular對象的module方法來聲明一個模組,模組的名字和ng-app的值對應。關於如何聲明、使用模組我們在後面會講。現在我們只要明白用這種方式可以優雅的讓ng運行起來就可以了。

四、模板與資料的綁定

  首先需要明確一下模板的概念。在我還不知道有模板這個東西的時候,曾經用js拼接出很長的HTML字串,然後append到頁面中,這種方式想想真是又土又笨。後來又看到可以把HTML程式碼封裝裹在一個<script>標籤中當作模板,然後按需要取來使用。在ng中,模板十分簡單,它就是我們頁面上的HTML代碼,不需要附加任何額外的東西。在模板中可以使用各種指令來增強它的功能,這些指令可以讓你把模板和資料巧妙的綁定起來。

  綁定這個東西可是ng中的大功臣了。在我們使用jQuery的時候,代碼中會大量充斥類似這樣的語句:var v = $(‘#id’).val();$(‘#id’).html(str);即頻繁的DOM操作(讀取和寫入),其實我們的最終目的並不是要操作DOM,而是要實現商務邏輯。ng的綁定將讓你擺脫DOM操作,只要模板與資料通過聲明進行了綁定,兩者將隨時保持同步,最新的資料會即時顯示在頁面中,頁面中使用者修改的資料也會即時被記錄在資料模型中。

  我構思了一個小例子,本篇文章的樣本將圍繞這個小例子來進行。我猜你已經厭倦了登入模組或者是購物車樣本,我們來點新穎的。我已化身為一名教師,我要用一個web應用來為學生出一份線上試題。首先從一道題開始吧~

<!DOCTYPE html><html ng-app="MyApp"><head><meta charset="utf-8" />  <title>模板資料繫結</title><script type="text/javascript" src="../angular.js"></script></head><body><div ng-controller="testC">    <h1>{{newtitle}}</h1>    題目:<input type="text" ng-model="name" /><br />    分數:<input type="text" ng-model="fraction" /><br />    <hr>    <h1>{{previewtitle}}</h1>    <b>{{name}}</b>({{fraction}}分)</div><script type="text/javascript" charset="utf-8">var app = angular.module(‘MyApp‘, [], function(){console.log(‘started‘)});var testC = function($scope){    $scope.newtitle = ‘建立試題‘;    $scope.previewtitle = ‘預覽試題‘;    $scope.name = $scope.fraction = ‘‘;}</script></body></html>

  頁面上有分別表示題目和分數的兩個輸入框,下面有一個即時預覽地區,用來展示題目的最終輸出效果。ng-controller=”textC”用來聲明一個需要和資料進行綁定的模板地區,它的範圍就是這個div之內的東西,並起名為textC,js代碼中定義了一個名為textC的函數與它對應,我們將在這個函數內完成綁定。函數傳入一個參數$scope,表示這個作用範圍。我們分別為作用範圍內的newtitle、previewtitle、name、fraction四個變數賦值。此時<h1>標籤內的{{}}便能得到相應的值了。

  通過{{}}只能完成資料向模板的單向綁定。要想進行雙向繫結,我們需要用到ng-modle這個指令,我們使用它分別為題目和分數進行了雙向繫結,這樣當輸入框內的值發生變化時,函數中的變數也會跟隨變化,它的變化會即時反饋在下方的預覽地區中,因為預覽地區中也有一個name和fraction的綁定。

  試試在下面的輸入框中進行編輯吧~

  我們並未進行任何DOM操作,架構自動完成了DOM的取值和賦值。在後面我將繼續完善這個例子,並引入更多的新概念。

五、模板中的一些控制方式

  我們在使用其他模板庫時,一般都會有模板的迴圈輸出、分支輸出、邏輯判斷等類似的控制。ng模板中都可以進行哪些控制呢?來一塊探索之。

    1.迴圈輸出

  繼續上面的例子。試題光有題目和分數還不夠,我想要出一道選擇題,可以自己添加若干選項並編輯選項內容,代碼該怎麼寫呢?先上代碼:

  HTML部分:

<div ng-controller="testC">    <h1>{{question.newtitle}}</h1>    題目:<input type="text" ng-model="question.name" /><br />    分數:<input type="text" number ng-model="question.fraction" /><br />    選項:<button ng-click="addOption()">增加選項</button><br />    <ul>        <li ng-repeat="o in question.options">            <b>{{$index+1}}.</b>            <input type="text" ng-model="o.content" value="o.content" />            <a href="javascript:void(0);" ng-click="delOption($index)">刪除</a>        </li>    </ul>    <hr>    <div >        <h1>{{question.previewtitle}}</h1>        <b>{{question.name}}</b>({{question.fraction}}分)        <ul>            <li ng-repeat="o in question.options">                <b>{{$index+1}}.</b>                <input type="radio" name="optcheck" />                {{o.content}}            </li>        </ul>    </div></div>

  js部分:

var app = angular.module(‘MyApp‘, [], function(){console.log(‘started‘)});var questionModel = {    newtitle : ‘建立試題‘,    previewtitle : ‘預覽試題‘,    name : ‘‘,    fraction : ‘‘,    options : []};app.controller(‘testC‘,function($scope){    $scope.question = questionModel;    $scope.addOption = function(){        var o = {content:‘‘};        $scope.question.options.push(o);    };    $scope.delOption = function(index){        $scope.question.options.splice(index,1);    };});

  請注意我的js代碼有了一點變化,在資料繫結時沒有直接寫成字串,而是將所有的資料寫成了一個questionModel對象,這隻是一個普遍的js對象,這樣能夠使我們的資料看起來像是“model”的樣子,畢竟我們是MV*嘛。那麼在模板中,我們使用name的地方也改成question.name,其他的值也同理。另外在controller部分,我沒有用var textC = function(){}這樣的寫法,而是改成了app.controller(‘textC’,function(){}),這樣明確指定了這個controller屬於MyApp這個模組,我們的MV*代碼看起來更專業、更一體了。

  在HTML部分我分別在建立地區和預覽地區添加了一個列表,用來放置各選項。並添加了一個“增加選項”按鈕。在<li>標籤上使用了ng-repeat指令來進行迴圈輸出,<li>標籤將會根據$scopt中的options數組長度被複製多份。在迴圈範圍內,可以使用$index獲得當前迴圈的索引,可以認為這是一個公開變數,直接使用。同時,迴圈輸出的每個<input>元素也使用ng-model與選項的內容進行了雙向繫結。另外還輸出一個刪除連結,點擊的時候調用$scope中的delOption方法。

  這樣綁定好之後,模版中的列表與資料模型中的options數組便保持了同步,我們在頁面上點擊增加選項,數組中便會增加一個元素,數組中的每個元素也會即時反饋在模板中。所以在js代碼中,我們只須操作options數組就夠了,壓根不需要在頁面上append或removed節點。

  你可以在下面點擊增加選項和刪除試試~

  2.單個節點的控制

  在上面的例子中,你是不是發現了,我在處理按鈕的點擊時,使用了叫做ng-click的指令,為什麼不直接用onclick呢?是因為ng根據自己的需要進行了封裝。我們把addOption這個函數定義在了controller範圍之內,用我們常規的onclick已經無法訪問到。換言之,我們頁面上的範圍,ng已經幫我們都規劃好了,我們只需按照它提供的方式來使用就夠了。

  通過onclick我們可以聯想到,HTML節點還有好多其他屬性,如class、style、href、checked、disabled等等,ng對這些都一一進行了封裝,更厲害的是,除此之外ng還額外提供了許多更加詳細的控制節點的指令。這些指令我以後會詳細研究,在這裡,我們先拿個其中一個應用到我們的例子中,看看效果先。

  我馬上變回老師身份,嘩~

  我建立試題的時候,不要局限於單選題,我想要在單選題和多選題之間能夠切換,同時下方的預覽地區內,選擇框也進行單選框和多選框的切換。上代碼:

  HTML部分:

<div ng-controller="testC">    <h1>{{question.newtitle}}</h1>    題目:<input type="text" ng-model="question.name" /><br />    分數:<input type="text" ng-model="question.fraction" /><br />    類型:<select ng-model="question.type"><option value="1" selected>單選</option><option value="2">多選</option></select><br />    選項:<button ng-click="addOption()">增加選項</button><br />    <ul>        <li ng-repeat="o in question.options">            <b>{{$index+1}}.</b>            <input type="text" ng-model="o.content" value="o.content" />            <a href="javascript:void(0);" ng-click="delOption($index)">刪除</a>        </li>    </ul>    <hr>    <div preview-panel>        <h1>{{question.previewtitle}}</h1>        <b>{{question.name}}</b>({{question.fraction}}分)        <ul>            <li ng-repeat="o in question.options">                <b>{{$index+1}}.</b>                <input type="radio" name="optcheck" ng-show="question.type==1" />                <input type="checkbox" ng-show="question.type==2" />                {{o.content}}            </li>        </ul>    </div></div>

  Js代碼中,我只是在questionModel中新增了一項type:1,表示題的類型,1為單選,2為多選。並給預設值為1.

var questionModel = {    newtitle : ‘建立試題‘,    previewtitle : ‘預覽試題‘,    name : ‘‘,    fraction : ‘‘,    type : ‘1‘,    options : []};

  在HTML中,我新增了一個下拉框,並與question.type建立雙向繫結。需要關注的是下面的預覽地區。我又添加了一個checkbox控制項來為多選題提供選擇框。顯然,radio與checkbox不能同時存在,所以我用ng-show這個指令來控制它們的顯隱,ng-show接收boolean類型的值以及計算結果為boolean類型的運算式。請注意,ng-show以及其他所有指令的值不是簡單的字串(儘管看上去是那樣),而是字串運算式,擁有計算能力,我現在的理解是它將來會在架構的某個地方放進eval()或是類似的函數執行。{{}}裡的內容也是一樣。

  所以在這裡我給radio的ng-show賦值為question.type==1,checkbox的ng-show賦值為question.type==2。當試題是單選題時,radio的ng-show便能得到值true,從而顯示出來。在下面看一下效果:

  其他的節點控制指令也可以類似這樣使用,思想就是根據你的商務邏輯,賦予它們一定的運算式。其他的控制還有事件綁定、表單控制項等等,篇幅的限制在這裡就不講了,以後專門開一篇介紹。

  3.過濾器(filter)

  所謂過濾器是指對輸出的內容進行格式化,如格式化為美元、日期等。架構自己提供一些過濾器,如排序、字串內容篩選。我們也可以自訂過濾器。

  過濾器在{{}}中使用,運算式後用|隔開使用。拿日期過濾器舉例,方式如下:

$scope.nowTime = new Date().valueOf();{{nowTime | date : ‘yyyy-MM-dd HH:mm:ss‘}}

  便會輸出格式化的日期。是不是很方便呢。

  接下來實戰一下,嘩~

  接上一步的例子,我想要在預覽地區中的題目前面顯示題型,像[單選題]這樣。我們的questionModel中,type的值是1和2,所以我們要做的就是通過過濾器,把1和2顯示為單選題和多選題。Go~

  在js中定義一個名為typeFilter的過濾器:

app.filter(‘typeFilter‘,function(){    var f = function(input){        return input == ‘1‘ ? ‘單選題‘ : ‘多選題‘;    }    return f;});

  filter函數如何使用以及執行細節不是本篇的討論內容,所以現在只要明白這樣可以定義一個過濾器就可以了。結構姑且認為是固定寫法,代碼不難看懂。

定義後這個filter後我們便可以在模板中使用了:

<b>[{{question.type | typeFilter}}]{{question.name}}</b>({{question.fraction}}分)

  在題目的前面顯示題型,並使用typeFilter,效果如下:

 

  單調的“1”已經華麗轉身變為了“單選題”。這隻是一個簡單的過濾器。你可以發揮想象力編寫更強大的過濾器。

六、指令(directive)

  前面已經提到很多次指令了,現在來正式介紹一下它。指令是ng為HTML補充的文法擴充,用於增強HTML的表現力。像我們之前使用的ng-controller、ng-model等都屬於指令。你也可以自訂指令。ng內部包含了一個強大的DOM解析引擎,所以這些新的標籤或是標籤屬性可以像使用原生HTML那樣很好的工作。

  聽起來很牛的樣子,那我們來試試自己定義一個指令吧。注意,我要變形了~

  我是教師,在建立試題輸入分數的時候應該只能輸入數字才對,輸入其他內容是不合法的,而且我希望這個分數是1~10之間的數字。能否只在輸入框上加一個屬性就完成這個驗證呢?就像使用HTML5新增的required一樣。

  我們定義一個叫做fractionNum的指令如下:

app.directive(‘fractionNum‘,function(){    return {        link : function(scope, elements, attrs, controller){            elements[0].onkeyup = function(){                if(isNaN(this.value) || this.value<1 || this.value>10){                    this.style.borderColor = ‘red‘;                }                else{                    this.style.borderColor = ‘‘;                }            };        }    };});

  哇,代碼好多層級呀,不要慌張,穩住陣腳!其實最後就是返回了帶有link欄位的對象,link的值是一個函數,用來定義指令的行為。從傳入的參數中可以擷取到當前元素,我們便可以拿當前元素開刀了。我在此處監聽當前元素的keyup事件,擷取元素的值,如果不是1~10之間的數字,則把輸入框的邊框顏色變為紅色。這下這個指令就可以工作了。

  至於傳入的四個參數到底都有什麼玄機,我暫時還未研究,也不是本篇的重點,本篇只是做一個概覽,先把ng拉出來溜溜的意思。現在姑且可以認為,一個指令的固定寫法大概就是這個結構。

  定義好的指令就可以在模板中使用了,使用方法如下:

分數:<input type="text" ng-model="question.fraction" fraction-num /><br />

  把它加在了分數輸入框上,此處要特別小心一個寫法,我定義的時候名字是fractionNum,用在模板中需要寫成fraction-num,就是因為名字中含有大寫字母的原因,感覺上跟使用css屬性名稱有點像。如果定義的時候沒有大寫字母,就不必擔心這一點了。

  看效果,現在你可以去下面蹂躪那個分數輸入框去了~

七、依賴注入

  通過依賴注入,ng想要推崇一種聲明式的開發方式,即當我們需要使用某一模組或服務時,不需要關心此模組內部如何?,只需聲明一下就可以使用了。在多處使用只需進行多次聲明,大大提高可複用性。

  比如我們的controller,在定義的時候用到一個$scope參數。

app.controller(‘testC‘,function($scope){});

  如果我們在此處還需操作其他的東西,比如與瀏覽器地址欄進行互動。我們只需再多添一個參數$location進去:

app.controller(‘testC‘,function($scope,$location){});

  這樣便可以通過$location來與地址欄進行互動了,我們僅僅是聲明了一下,所需的其他代碼,架構已經幫我們注入了。我們很明顯的感覺到了這個函數已經不是常規意義上的javascript函數了,在常規的函數中,把形參換一個名字照樣可以運行,但在此處若是把$scope換成別的名字,程式便不能運行了。因為這是已經定義好的服務名稱。

  這便是依賴注入機制。順理成章的推斷,我們可以自己定義模組和服務,然後在需要的地方進行聲明,由架構來替我們注入。

  對,我的小例子呢?我現在要想點需求來把依賴注入實驗一下。我覺得試題全是文字太單調了,我希望題目中能含有圖片/音視頻,或者選項中可以含有圖片/音視頻。並且,我要更靈活一點,我要為試題提供若干套模板來選擇,選擇不同的模板可以建立不同樣式的題,比如模板一為純文字試題、模板二為題目中帶圖片/音視頻的試題、模板三為選項中帶圖片/音視頻的試題,等等。注意此處所說的模板跟ng的模板不是一個概念,是我自己試題的模板,不要混淆。這些模板應該是與試題相分離的,以後可以為其他題型例如填空題啊簡答題啊同樣使用。所以試題模板這個東西就需要做成服務了。

  不知道我表達清楚了沒有,確實有點繞。來看下我們如何定義一個服務:

app.factory(‘tpls‘,function(){    return [‘tpl1‘,‘tpl2‘,‘tpl3‘,‘tpl4‘];});

  看上去相當簡單,是因為我在這裡僅僅是直接返回一個數組。在實際應用中,這裡應該是需要向伺服器發起一個請求,來擷取到這些模板們。服務的定義方式有好幾種,包括使用provider方法、使用factory方法,使用service方法。它們之間的區別暫且不關心。我們現在只要能建立一個服務出來就可以了。我使用了factory方法。一個需要注意的地方是,架構提供的服務名字都是由$開頭的,所以我們自己定義的最好不要用$開頭,防止發生命名衝突。

  定義好一個服務後,我們就可以在控制器中聲明使用了,如下:

app.controller(‘testC‘,function($scope,tpls){    $scope.question = questionModel;    $scope.nowTime = new Date().valueOf();    $scope.templates = tpls; //賦值到$scope中    $scope.addOption = function(){        var o = {content:‘‘};        $scope.question.options.push(o);    };    $scope.delOption = function(index){        $scope.question.options.splice(index,1);    };});

  此時,若在模板中書寫如下代碼,我們便可以擷取到服務tpls所提供的資料了:

模板:<a href="javascript:void(0);" ng-repeat="t in templates">{{t}}&nbsp;&nbsp;</a><br />

  隨著知識點的一點點增加,我的這個小例子也越來越豐滿了,來看看完整版吧:

 

  查看完整代碼請移步到runjs:http://runjs.cn/code/95wlwsfh

八、總結一下

    乎~可以鬆一口氣了。文章寫到這裡終於接近尾聲了,不知我上面的陳述能否被大家理解。ng所包含的內容還是挺多的,以上的每個概念都可以再拆出幾篇文章來講解。所以我在這裡只能是每一個都點到為止,不揪細節。這篇文章的思想也就是“先瞭解概念,例子能跑起來就行了”。在以後的文章中會隨著我學習的深入進行探討。

轉載

走進AngularJs(一)angular基本概念的認識與實戰

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.