原版地址: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
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中作如下的事情:
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中工做的,讓咱們使用如下組件建立一個小應用:
在咱們的模版裏面的消息,包含一個到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>
在上面例子中須要注意的東東:
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被建立:
繼承的工做,在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>