AngularJs學習筆記--Understanding the Controller Component

原版地址:http://docs.angularjs.org/guide/dev_guide.mvc.understanding_modeljavascript

  

  在angular中,controller是一個javascript 函數(type/class),被用做擴展除了root scope在外的angular scope(http://www.cnblogs.com/lcllao/archive/2012/09/23/2698651.html)的實例。當咱們或者angular經過scope.$new API(http://docs.angularjs.org/api/ng.$rootScope.Scope#$new)建立新的child scope時,有一個選項做爲方法的參數傳入controller(這裏沒看明白,只知道controller的第一個參數是一個新建立的scope,有綁定parent scope)。這將告訴angular須要聯合controller和新的scope,而且擴展它的行爲。css

  controller能夠用做:html

  • 設置scope對象的初始狀態。
  • 增長行爲到scope中。

 

1、 Setting up the initial state of a scope object(設置scope對象的初始狀態) java

  一般,當咱們建立應用的時候,咱們須要爲angular scope設置初始化狀態。angularjs

  angular將一個新的scope對象應用到controller構造函數(估計是做爲參數傳進去的意思),創建了初始的scope狀態。這意味着angular從不建立controller類型實例(即不對controller的構造函數使用new操做符)。構造函數一直都應用於存在的scope對象。chrome

  咱們經過建立model屬性,創建了scope的初始狀態。例如:express

function GreetingCtrl ($scope) {$scope.greeting = 「Hola!」;}

  「GreetingCtrl」這個controller建立了一個叫「greeting」的,能夠被應用到模版中的model。bootstrap

 

2、 Adding Behavior to a Scope Object(在scope object中增長行爲)api

  在angular scope對象上的行爲,是以scope方法屬性的形式,供模版、視圖使用。這行爲(behavior)能夠修改應用的model。mvc

  正如指引的model章節(http://www.cnblogs.com/lcllao/archive/2012/09/24/2699861.html)討論的那樣,任意對象(或者原始的類型)賦值到scope中,成爲了model屬性。任何附加到scope中的function,對於模版視圖來講都是可用的,能夠經過angular expression調用,也能夠經過ng event handler directive調用(如ngClick)。

 

3、 Using Controllers Correctly

  通常而言,controller不該該嘗試作太多的事情。它應該僅僅包含單個視圖所須要的業務邏輯(還有點沒轉過彎了,一直認爲Controller就是個作轉發的……)。

  保持Controller的簡單性,常見辦法是抽出那些不屬於controller的工做到service中,在controller經過依賴注入來使用這些service。這些東西會在嚮導的Dependency Injection Services章節中討論。

  不要在Controller中作如下的事情:

  • 任何類型的DOM操做 - controller應該僅僅包含業務邏輯。DOM操做,即應用的表現邏輯,它的測試難度是衆所周知的。將任何表現邏輯放到controller中,大大地影響了應用邏輯的可測試性。angular爲了自動操做(更新)DOM,提供的數據綁定(http://docs.angularjs.org/guide/dev_guide.templates.databinding)。若是咱們但願執行咱們自定義的DOM操做,能夠把表現邏輯抽取到directive(http://www.cnblogs.com/lcllao/archive/2012/09/09/2677190.html)中。
  • Input formatting(輸入格式化) - 使用angular form controls (http://www.cnblogs.com/lcllao/archive/2012/09/17/2688127.html)代替。
  • Output filtering (輸出格式化過濾) - 使用angular filters 代替。
  • 執行無狀態或有狀態的、controller共享的代碼 - 使用angular services 代替。
  • 實例化或者管理其餘組件的生命週期(例如建立一個服務實例)。

 

4、 Associating Controllers with Angular Scope Objects

  咱們能夠顯式地經過scope.$new關聯controller和scope對象,或者隱式地經過ngController directive(http://docs.angularjs.org/api/ng.directive:ngController)或者$route service(http://docs.angularjs.org/api/ng.$route)。

  1. Controller 構造函數和方法的 Example

  爲了說明controller組件是如何在angular中工做的,讓咱們使用如下組件建立一個小應用:

  • 一個有兩個按鈕和一個簡單消息的template。
  • 一個由名爲」spice」的字符串屬性組成的model。
  • 一個有兩個設置spice屬性的方法的controller。

  在咱們的模版裏面的消息,包含一個到spice model的綁定,默認設置爲」very」。根據被單擊按鈕,將spice model的值設置爲」chili」或者」 jalapeño」,消息會被數據綁定自動更新。

 
<!DOCTYPE html>
<html ng-app>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>spicy-controller</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body class="ng-cloak">
    <div ng-controller="SpicyCtrl">
        <button ng-click="chiliSpicy()">Chili</button>
        <button ng-click="jalapenoSpicy('jalapeño')">Jalapeño</button>
        <p>The food is {{spice}} spicy!</p>
    </div>

    <script src="../angular-1.0.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        function SpicyCtrl($scope) {
            $scope.spice = "very";
            $scope.chiliSpicy = function() {
                $scope.spice = "chili";
            };
            $scope.jalapenoSpicy = function(val) {
                this.spice = val;
            };
        }
    </script>
</body>
</html>
 

 

  在上面例子中須要注意的東東:

  • ngController directive被用做爲咱們的模版(隱式)建立scope,那個scope會稱爲SpicyCtrl的參數。
  • SpicyCtrl只是一個普通的javascript function。做爲一個(隨意)的命名規則,名稱以大寫字母開頭,並以」Ctrl」或者」Controller」結尾。
  • 對屬性賦值能夠建立或者更新$scope的model。
  • controller方法能夠經過直接分配到$scope實現建立。(chiliSpicy方法)
  • controller的兩個方法在template中都是可用的(在ng-controller屬性所在的元素以及其子元素中都有效)。
  • 注意:以前版本的angular(1.0RC以前)容許咱們使用this來代替$scope定義$scope的方法,但這裏再也不適用。在定義在scope上的方法中,this跟$scope是等價的(angular將this至爲scope),但不是在咱們的controller構造函數中。
  • 注意:以前版本的angular(1.0RC以前),會自動增長controller的prototype方法到scope中,但如今不會了。全部方法都須要人工加入到scope中。(印象中以前有一個guide,有用過這個。還沒更新-_-!)

  

  controller方法能夠帶參數的,正以下面例子所示:

 
<!DOCTYPE html>
<html ng-app>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>controller-method-aruments</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body class="ng-cloak">
    <div ng-controller="SpicyCtrl">
        <input ng-model="customSpice" value="wasabi"/>
        <button ng-click="spicy(customSpice)">customSpice</button>
        <br/>
        <button ng-click="spicy('Chili')">Chili</button>
        <p>The food is {{spice}} spicy!</p>
    </div>

    <script src="../angular-1.0.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        function SpicyCtrl($scope) {
            $scope.spice = "very";
            $scope.spicy = function(spice) {
                $scope.spice = spice;
            };
        }
    </script>
</body>
</html>
 

  注意那個SpicyCtrl controller如今只定義了一個有一個參數」spice」、叫」spicy」的方法。template能夠引用controller方法併爲它傳遞常量字符串或model值。

 

  Controller繼承在angular是基於scope繼承的。讓咱們看看下面的例子:

 
<!DOCTYPE html>
<html ng-app>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>controller-inheritance</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body class="ng-cloak">
    <div ng-controller="MainCtrl">
        <p>Good {{timeOfDay}}, {{name}}!</p>
        <div ng-controller="ChildCtrl">
            <p>Good {{timeOfDay}}, {{name}}!</p>
            <p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
        </div>
    </div>

    <script src="../angular-1.0.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        function MainCtrl($scope) {
            $scope.timeOfDay = 'Main時間';
            $scope.name = 'Main名稱';
        }

        function ChildCtrl($scope) {
            $scope.name = 'Child名稱';
        }

        function BabyCtrl($scope) {
            $scope.timeOfDay = 'Baby時間';
            $scope.name = 'Baby名稱';
        }
    </script>
</body>
</html>
 

  注意咱們如何嵌套3個ngController directive到模版中的。爲了咱們的視圖,這模版結構將會致使4個scope被建立:

  • root scope。
  • MainCtrl scope,包含timeOfDay和name model。
  • ChildCtrl scope,覆蓋了MainCtrl scope的name model,繼承了timeOfDay model。
  • BabyCtrl scope,覆蓋了MainCtrl scope 的timeOfDay以及ChildCtrl scope的name。

  繼承的工做,在controller和model中是同樣的。因此咱們前一個例子中,全部model能夠經過controller被重寫。

  注意:在兩個Controller之間標準原型繼承不是如咱們所想地那樣工做的,由於正如咱們以前提到的,controller不是經過angular直接初始化的,但相反地,apply了那個scope對象。(controllers are not instantiated directly by angular, but rather are applied to the scope object,這裏跟以前同樣,我仍是沒理解。)

 

5、 Testing Controller

  雖然有不少方法去測試controller,最好的公約之一,以下面所示,須要注入$rootScope和$controller。(測試須要配合jasmine.js)

 
<!DOCTYPE html>
<html ng-app>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>controller-test</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <link rel="stylesheet" href="../jasmine.css">
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body class="ng-cloak">

<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script src="../angular-scenario-1.0.1.js" type="text/javascript"></script>
<script src="../jasmine.js" type="text/javascript"></script>
<script src="../jasmine-html.js" type="text/javascript"></script>
<script src="../angular-mocks-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
    function MyController($scope) {
        $scope.spices = [
            {"name":"pasilla", "spiciness":"mild"},
            {"name":"jalapeno", "spiceiness":"hot hot hot!"},
            {"name":"habanero", "spiceness":"LAVA HOT!!"}
        ];

        $scope.spice = "habanero";
    }
    describe("MyController function", function () {
        describe("MyController", function () {
            var scope;
            beforeEach(inject(function ($rootScope, $controller) {
                scope = $rootScope.$new();
                var ctrl = $controller(MyController, {$scope:scope});
            }));

            it('should create "cpices" model with 3 spices', function () {
                expect(scope.spices.length).toBe(3);
            });

            it('should set the default value of spice', function () {
                expect(scope.spice).toBe("habanero");
            });
        });
    });

    (function () {
        var jasmineEnv = jasmine.getEnv();
        jasmineEnv.updateInterval = 1000;

        var trivialReporter = new jasmine.TrivialReporter();

        jasmineEnv.addReporter(trivialReporter);

        jasmineEnv.specFilter = function (spec) {
            return trivialReporter.specFilter(spec);
        };

        var currentWindowOnload = window.onload;

        window.onload = function () {
            if (currentWindowOnload) {
                currentWindowOnload();
            }
            execJasmine();
        };

        function execJasmine() {
            jasmineEnv.execute();
        }

    })();

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

 

  若是咱們須要測試嵌套的controller,咱們須要在test中建立與DOM裏面相同的scope繼承關係。

 
<!DOCTYPE html>
<html ng-app>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>controller-test</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <link rel="stylesheet" href="../jasmine.css">
    <style type="text/css">
        .ng-cloak {
            display: none;
        }
    </style>
</head>
<body class="ng-cloak">

<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script src="../angular-scenario-1.0.1.js" type="text/javascript"></script>
<script src="../jasmine.js" type="text/javascript"></script>
<script src="../jasmine-html.js" type="text/javascript"></script>
<script src="../angular-mocks-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
    function MainCtrl($scope) {
        $scope.timeOfDay = 'Main時間';
        $scope.name = 'Main名稱';
    }

    function ChildCtrl($scope) {
        $scope.name = 'Child名稱';
    }

    function BabyCtrl($scope) {
        $scope.timeOfDay = 'Baby時間';
        $scope.name = 'Baby名稱';
    }

    describe("MyController", function () {
        var mainScope,childScope,babyScope;
        beforeEach(inject(function ($rootScope, $controller) {
            mainScope = $rootScope.$new();
            var mainCtrl = $controller(MainCtrl, {$scope:mainScope});
            childScope = mainScope.$new();
            var childCtrl  = $controller(ChildCtrl, {$scope:childScope});
            babyScope = childScope.$new();
            var babyCtrl  = $controller(BabyCtrl, {$scope:babyScope});
        }));

        it('should have over and selected', function () {
            expect(mainScope.timeOfDay).toBe("Main時間");
            expect(mainScope.name).toBe("Main名稱");
            expect(childScope.timeOfDay).toBe("Main時間");
            expect(childScope.name).toBe("Child名稱");
            expect(babyScope.timeOfDay).toBe("Baby時間");
            expect(babyScope.name).toBe("Baby名稱");
        });
    });

    (function () {
        var jasmineEnv = jasmine.getEnv();
        jasmineEnv.updateInterval = 1000;

        var trivialReporter = new jasmine.TrivialReporter();

        jasmineEnv.addReporter(trivialReporter);

        jasmineEnv.specFilter = function (spec) {
            return trivialReporter.specFilter(spec);
        };

        var currentWindowOnload = window.onload;

        window.onload = function () {
            if (currentWindowOnload) {
                currentWindowOnload();
            }
            execJasmine();
        };

        function execJasmine() {
            jasmineEnv.execute();
        }

    })();

</script>
</body>
</html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  1. AngularJs學習筆記--bootstrap
  2. AngularJs學習筆記--html compiler
  3. AngularJs學習筆記--concepts
  4. AngularJs學習筆記--directive
  5. AngularJs學習筆記--expression
  6. AngularJs學習筆記--Forms
  7. AngularJs學習筆記--I18n/L10n
  8. AngularJs學習筆記--IE Compatibility
  9. AngularJs學習筆記--Modules
  10. AngularJs學習筆記--Scope
  11. AngularJs學習筆記--Dependency Injection
  12. AngularJs學習筆記--Understanding the Model Component
  13. AngularJs學習筆記--Understanding the Controller Component
  14. AngularJs學習筆記--E2E Testing
  15. AngularJs學習筆記--Understanding Angular Templates
  16. AngularJs學習筆記--Using $location
  17. AngularJs學習筆記--Creating Services
  18. AngularJs學習筆記--Injecting Services Into Controllers
  19. AngularJs學習筆記--Managing Service Dependencies
  20. AngularJs學習筆記--unit-testing
相關文章
相關標籤/搜索