AngularJs學習筆記--concepts(概念)

啓動(Startupjavascript

下面描述angular是如何啓動的(參考圖表與下面的例子):css

1. 瀏覽器加載HTML,將HTML標籤轉換爲DOM對象;html

2. 瀏覽器加載angular.js的腳本;java

3. Angular等待DOMContentLoaded事件;node

4. Angular尋找ng-app這個用於指定應用邊界範圍的directivejson

5. 若是ng-app有指定module(也許是ng-app=」SomeApp」),將被用做配置$injector數組

6. $injector用於建立$compile服務(service)以及$rootScope瀏覽器

7. $compile服務用做「編譯」(有點像遍歷,而後作一點神祕的事情)DOM,並將其與對應的$rootScope鏈接。app

8. ng-init 這個directive在對應的scope中建立name屬性並對其賦予」Kitty」值;框架

        9. 將「{{name}}」的值插入(interpolates)到表達式中,最終顯示」Hello Kitty!」。

<!DOCTYPE html>
<html ng-app>
<head>
    <meta charset="UTF-8">
    <title>Hello Kitty!</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body>
<div class="ng-cloak" ng-init="name='Kitty'">Hello {{name}}!</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
</body>
</html>

Runtime


這圖表和後面的例子,描述了angular如何經過瀏覽器event-loop(全部的時間處理函數,以及timer執行的函數,會排在一個queue結構中,利用一個無限的循環,不斷從queue中取出函數來執行,這個就是event-loop。來自http://wiki.nodejs.tw/nodejs_from_scratch/javascript-yunodejs/2-1-event-loop)來進行交互。

  1. 瀏覽器event-loop等待事件到來。事件來自於用戶交互(DOM events)、timer事件(setTimeout)、network事件(服務端響應,XHR之類);

  2. 事件回調函數開始執行。這裏進入javascript上下文(context)。這回調函數能夠修改DOM結構。

  3. 當回調函數執行完畢後,瀏覽器退出javascript context,根據DOM的改變來重繪視圖。

  Angular經過建立本身的事件處理循環(event processing loop),修改了通常的javascript流(flow)。這將Javascript分割成傳統的和Angular的執行上下文(execution context)。只要是在Angular execution context 裏面執行的操做,都擁有angular data-binding、異常處理(exception handling)、屬性監視(property watching)等能力。咱們能夠經過在javascript使用$apply(),進入Angular execution context。但要記住一點,在大多數(angular的)地方(如controllersservices),處理事件的directive會爲你調用$apply。手動調用$apply的場景,通常是當你實現自定義事件處理函數,或者處理第三方庫的回調的時候。

  1. 經過調用scope.$apply(stimulusFn)進入angular execution contextstimulusFn就是咱們想在angular execution context中執行的函數(含scope做爲參數)或者angular合法的表達式。

  2. Angular執行stimulusFn,這一般會改變應用的狀態(application state)。

  3. Angular進入$digest loop。這個loop由一個處理$evalAsync queue 和處理$watch list兩個更小的循環組成。$digest loop會在model穩定以前保持迭代,即$evalAsync queue爲空,並且$watch list沒有檢測到任何變化。

  4. $evalAsync queue被用做安排必須跳出當前堆棧幀(堆棧幀指的是在堆棧中爲當前正在運行的函數分配的區域(或空間)。傳入的參數、返回地址(當這個函數結束後必須跳轉到該返回地址。譯註:即主調函數的斷點處)以及函數所用的內部存儲單元(即函數存儲在堆棧上的局部變量)都在堆棧幀中。http://book.51cto.com/art/200804/70915.htm C.1.1  堆棧幀)以外,但在瀏覽器視圖繪製以前的工做。這一般是經過使用setTimeout(0)來實現。但setTimeout(0)這方法,會致使緩慢,或者在每一個事件處理完畢後,瀏覽器繪製視圖時,出現視圖閃爍(angular有沒有去解決這個問題?如何解決?)。

  5. $watch list是有可能在最近一次迭代中被修改的表達式的集合。若是(model)發生了改變,那麼$watch 函數會被調用,從而達到對特定的DOM從新賦值的目標。

  6. 一旦Angular $digest loop 完成了(以前3提到的狀況),離開angularjavascriptcontext後,瀏覽器緊跟着就會去重繪DOM,以響應變化。

  下面解釋例子「Hello Kitty(-_-!)是如何在用戶在文本框輸入文本時實現數據綁定(data-binding)效果。

  1. 編譯階段(compilation phase)

    a) ng-modelinput directive<input>中版定keydown事件監聽器。

    b) {{name}}佔位符(interpolation,不知道怎麼翻譯)(表達式)設置一個$watch以便在name發生改變時有所響應。

  2. 執行階段(runtime phase)

    a) 在inut控件中按下」X」按鈕,讓瀏覽器觸發一個keydown事件;

    b) input directive捕捉到文本框值的改變,而後調用$apply(「name = ‘X’;」),在angular execution context中更新應用的model

    c) Angluar將 「name = ‘X’;」應用在model中。(model發生改變)

    d) $digest loop開始

    e) $watch list檢測到name的值被改變了,而後再次解析{{name}}表達式,而後更新DOM

    f) Angulart退出(angular) execution context,再依次退出keydown事件以及javascript execution context

    g) 瀏覽器重繪視圖,更新字符。

<!DOCTYPE html>
<html ng-app>
<head>
    <meta charset="UTF-8">
    <title>Hello Kitty!</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body>
    <input class="ng-cloak" ng-model="name"/>
    <p>Hello {{name}}!</p>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
</body>
</html>

Scope

  scope的是負責檢測model的變化,並做爲表達式的執行上下文(execution context)Scope是在一個相似於DOM結構的層次結構中嵌套的(據以前瞭解,劃分可能跟controller有關)。(詳情查看individual directive documentation,看看哪一個directive會建立新的scope

  下面的例子展現」name」這個表達式的值是根據它依賴(所屬)的scope決定的,並且還包含了值查找的方式(相似Js的做用域鏈)。

<!DOCTYPE HTML>
<html ng-app>
<head>
    <meta charset="UTF-8">
    <title>scope</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body>
<div ng-controller="ControllerA">
    Hello {{name}}!;
</div>
<div ng-controller="ControllerB">
    Hello {{name}}!;
    <div ng-controller="ControllerC">
        Hello {{name}}!;
        <div ng-controller="ControllerD">
            Hello {{name}}!;
        </div>
    </div>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
    function ControllerA($scope) {
        $scope.name = 'Kitty';
    }

    function ControllerB($scope) {
        $scope.name = 'Lcllao';
    }

    function ControllerC($scope) {
        $scope.name = 'Jeffrey';
    }

    function ControllerD($scope) {

    }
</script>
</body>
</html>

Controller


<!DOCTYPE HTML>
<html ng-app>
<head>
    <meta charset="UTF-8">
    <title>Controller</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body>
<div ng-controller="ControllerA">
    Hello {{name}}!
    <button ng-click="doIt()">DoIt!!</button>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
    function ControllerA($scope) {
        $scope.name = 'Kitty';
        $scope.doIt = function() {
            $scope.name = "Handsome";
        };
    }
</script>
</body>
</html>

Controller是在view背後的代碼。它的職責是構建model,並經過回調函數,將其(model)推送到view中。View是當前scopetemplate(HTML)的映射Scope是指揮modelview以及向controller發送event的紐帶。

  Controllerview分離是很重要的,由於:

  • Controller是寫在javascript中的。Javascript是命令式的(imperative)。命令(imperative)是描述應用程序行爲的一個好方法。Controller不該該包含任何顯示信息(的邏輯)(DOM引用或者HTML片斷)

  • View模版是寫在HTML裏的。HTML是聲明式的。聲明式(的HTML)是描述UI的好方法。View不該該包含任何行爲。

  • 因爲Controller不知道本身須要對應哪個View,使得一個Controller能夠(間接)使用多個View。這對於re-skinning(更換皮膚?)、其餘設備特定的視圖(例如手機與桌面)還有代碼的可測性是很重要的。

Model


Model,能夠理解爲數據對象。它被用做與模版結合,以產生視圖。爲了將model寫入到視圖中,model必須被scope所引用。與不少其餘框架不一 樣,angular對model沒有任何限制與要求。不須要額外添加class,也不須要經過特殊的特權方法去訪問或者改變model。Model的數據 類型,能夠是原始的類型(string、number……),能夠是鍵值對象({a:1,b:2}),也能夠是函數(function() {…})。簡要地說,angular的model只須要是一個普通的javascript對象。

View   

     view是用戶所能看到的東西。view誕生於模版。它與model結合,最終呈現爲瀏覽器DOM。Angular採起一個對於其餘不少模版系統來講,很不同的方式去呈現View。


  • 其餘模版引擎:不少模版引擎,是經過創建帶有特殊標記的HTML字符串來實現的。一般這些模版標記破壞了HTML的語法,這意味着不能經過通常的 HTML編輯器去編輯代碼(這個嘛…)。模版字符串傳入模版引擎,與數據合併。最終生成HTML字符串。這些字符串通常經過.innerHTML的方式寫 入DOM中,促使瀏覽器呈現模版內容。當數據發生改變時,這個過程須要一次又一次地重複。模版的粒度與DOM更新的粒度一致。這粒的關鍵,是模版系統處理 字符串。

  • Angular:Angular模版的不一樣之處,在於它是基於DOM的而不是基於字符串的。模版依然須要在HTML中寫入一些字符串,但依舊是 HTML(不是經過在裏面嵌入模版)。瀏覽器把HTML轉換爲DOM,而後DOM成爲了compiler(angular的模版引擎)的輸入。 Compiler查找directives,依次在model中設置watches。得出的結果,是一個一直更新的view,不須要從新拼接model與 template。model成爲了view的惟一數據來源(single source of truth)。

Directives

  Directive是一個行爲(例如以前文章的例子「躲貓貓」)或DOM轉換(自定義標籤,裏面包含一組DOM),將其名稱放在屬性、標籤名、class名裏面均可以觸發該directiveDirective容許你以聲明的方式擴展HTML的標籤。

  下面的例子,還有一些疑問。就是$render如何觸發

<!DOCTYPE HTML>
<html ng-app="myDirective">
<head>
    <meta charset="UTF-8">
    <title>directive</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body ng-controller="MyCtrl">
<div ng-model="content" contenteditable="true">My Little Dada</div>
<pre>modelValue = {{content}}</pre>
<button ng-click="reset()">reset(change model)</button>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
    angular.module("myDirective",[])
            .directive("contenteditable",function() {
                return {
                    require:'ngModel',
                    link:function (scope, element, attr, ngModel) {
                        function setVal() {
                            ngModel.$setViewValue(element.text());
                        }

                        // veiw -> model
                        element.bind("keyup",function() {
                            scope.$apply(setVal);
                        });
                        // model -> view
                        ngModel.$render = function(val) {
                            console.log("render running");
                            element.html(val);
                        };
                        //init
                        setVal();
                    }
                }
            }
    ).controller("MyCtrl",function($scope) {
                $scope.reset = function() {
                        $scope.content = "My Little Dada";
                };
            });
</script>
</body>
</html>

Filters

  Filters 扮演一個數據轉換(格式化)的角色。一般他們是與地域有關的,不一樣地域也許會有不一樣的輸出格式。他們在追隨了Unix過濾器的精神與相似的語法:|  (pipe)

<!DOCTYPE HTML>
<html ng-app>
<head>
    <meta charset="UTF-8">
    <title>filter</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body>
<div ng-init="list = ['百度B','搜狗S','360','3SB']">

    數字格式化: 1233211234567 -> {{1233211234567|number}}<br/>
    數組過濾,而後經過json格式輸出: <input ng-model="myFilterText" type="text"/><br/>
    {{list|filter:myFilterText|json}}<br/>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
</body>
</html>

Modules and the Injector


Injector是一個服務定位器。每個Angular應用,都會有一個單獨的injectorInjector提供一個經過名稱查找對象實例的途徑。Injector會在內部cache中保持全部對象實例,因此重複調用相同的名稱時,返回的都是同一個對象實例。若是對象不存在,那麼它會請求實例工廠(instance factory)去建立一個新實例。  Module是一個配置injector的實例工廠的方法,被稱爲」provider」。

// Create a module
    var myModule = angular.module('myModule', [])
     
    // Configure the injector
    myModule.factory('serviceA', function() {
    return {
    // instead of {}, put your object creation here
    };
    });
     
    // create an injector and configure it from 'myModule'
    var $injector = angular.injector('myModule');
     
    // retrieve an object from the injector by name
    var serviceA = $injector.get('serviceA');
     
    // always true because of instance cache
    $injector.get('serviceA') === $injector.get('serviceA');//true

可是injector的真正牛X的地方在於它能夠用於調用方法和」instantiate」 type。這個美妙的特性是容許methodtypes請求他們所依賴的資源,而不是尋找他們。

// You write functions such as this one.
    function doSomething(serviceA, serviceB) {
    // do something here.
    }
     
    // Angular provides the injector for your application
    var $injector = ...;
     
    ///////////////////////////////////////////////
    // the old-school way of getting dependencies.
    var serviceA = $injector.get('serviceA');
    var serviceB = $injector.get('serviceB');
     
    // now call the function
    doSomething(serviceA, serviceB);
     
//上面是傳統的老方法~下面是angular說本身的牛X方法

    ///////////////////////////////////////////////
    // the cool way of getting dependencies.
    // the $injector will supply the arguments to the function automatically
    $injector.invoke(doSomething); // This is how the framework calls your functions

注意,咱們惟一須要寫的,就是咱們的function,在functionarguments中列出方法依賴的資源便可!當angular調用function時,他會使用」call」方法,自動填充function agruments  留意下面的例子中是如何在constructor中列出依賴的。當ng-controller實例化controller時,將自動提供所依賴的資源。沒有必要去建立、尋找、建立injector引用來加載依賴資源

<!DOCTYPE HTML>
<html ng-app="timeExample">
<head>
    <meta charset="UTF-8">
    <title>injector</title>
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body>
<div ng-controller="ClockCtrl">
    Current time is : {{time.now}}
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
    angular.module("timeExample", []).factory("myClock", function ($timeout) {
        var time = {};
        (function tick() {
            time.now = new Date().toString();
            $timeout(tick, 1000);
        })();
        return time;
    });
    /**
     *
     * @param $scope
     * @param myClock 這裏自動插入了依賴的myClock!!
     * @constructor
     */
    function ClockCtrl($scope,myClock) {
        $scope.time = myClock;
    }
</script>
</body>
</html>

11、Angular Namespace

爲了防止名稱衝突,angular會在object的名稱中加入前綴$。請不要在代碼中使用$前綴以免衝突。

相關文章
相關標籤/搜索