AngularJs Understanding the Controller Component
在angular中,controller是一個javascript 函數(shù)(type/class),被用作擴展除了root scope在外的angular scope(http://www.dbjr.com.cn/article/91749.htm)的實例。當(dāng)我們或者angular通過scope.$new API(http://docs.angularjs.org/api/ng.$rootScope.Scope#$new)創(chuàng)建新的child scope時,有一個選項作為方法的參數(shù)傳入controller(這里沒看明白,只知道controller的第一個參數(shù)是一個新創(chuàng)建的scope,有綁定parent scope)。這將告訴angular需要聯(lián)合controller和新的scope,并且擴展它的行為。
controller可以用作:
1.設(shè)置scope對象的初始狀態(tài)。
2.增加行為到scope中。
一、 Setting up the initial state of a scope object(設(shè)置scope對象的初始狀態(tài))
通常,當(dāng)我們創(chuàng)建應(yīng)用的時候,我們需要為angular scope設(shè)置初始化狀態(tài)。
angular將一個新的scope對象應(yīng)用到controller構(gòu)造函數(shù)(估計是作為參數(shù)傳進去的意思),建立了初始的scope狀態(tài)。這意味著angular從不創(chuàng)建controller類型實例(即不對controller的構(gòu)造函數(shù)使用new操作符)。構(gòu)造函數(shù)一直都應(yīng)用于存在的scope對象。
我們通過創(chuàng)建model屬性,建立了scope的初始狀態(tài)。例如:
function GreetingCtrl ($scope) {$scope.greeting = “Hola!”;}
“GreetingCtrl”這個controller創(chuàng)建了一個叫“greeting”的,可以被應(yīng)用到模版中的model。
二、 Adding Behavior to a Scope Object(在scope object中增加行為)
在angular scope對象上的行為,是以scope方法屬性的形式,供模版、視圖使用。這行為(behavior)可以修改應(yīng)用的model。
正如指引的model章節(jié)(http://www.dbjr.com.cn/article/91777.htm)討論的那樣,任意對象(或者原始的類型)賦值到scope中,成為了model屬性。任何附加到scope中的function,對于模版視圖來說都是可用的,可以通過angular expression調(diào)用,也可以通過ng event handler directive調(diào)用(如ngClick)。
三、 Using Controllers Correctly
一般而言,controller不應(yīng)該嘗試做太多的事情。它應(yīng)該僅僅包含單個視圖所需要的業(yè)務(wù)邏輯(還有點沒轉(zhuǎn)過彎了,一直認為Controller就是個做轉(zhuǎn)發(fā)的……)。
保持Controller的簡單性,常見辦法是抽出那些不屬于controller的工作到service中,在controller通過依賴注入來使用這些service。這些東西會在向?qū)У腄ependency Injection Services章節(jié)中討論。
不要在Controller中做以下的事情:
- 任何類型的DOM操作 - controller應(yīng)該僅僅包含業(yè)務(wù)邏輯。DOM操作,即應(yīng)用的表現(xiàn)邏輯,它的測試難度是眾所周知的。將任何表現(xiàn)邏輯放到controller中,大大地影響了應(yīng)用邏輯的可測試性。angular為了自動操作(更新)DOM,提供的數(shù)據(jù)綁定(http://docs.angularjs.org/guide/dev_guide.templates.databinding)。如果我們希望執(zhí)行我們自定義的DOM操作,可以把表現(xiàn)邏輯抽取到directive(http://www.dbjr.com.cn/article/91739.htm)中。
- Input formatting(輸入格式化) - 使用angular form controls (http://www.dbjr.com.cn/article/91744.htm)代替。
- Output filtering (輸出格式化過濾) - 使用angular filters 代替。
- 執(zhí)行無狀態(tài)或有狀態(tài)的、controller共享的代碼 - 使用angular services 代替。
- 實例化或者管理其他組件的生命周期(例如創(chuàng)建一個服務(wù)實例)。
四、 Associating Controllers with Angular Scope Objects
我們可以顯式地通過scope.$new關(guān)聯(lián)controller和scope對象,或者隱式地通過ngController directive(http://docs.angularjs.org/api/ng.directive:ngController)或者$route service(http://docs.angularjs.org/api/ng.$route)。
1. Controller 構(gòu)造函數(shù)和方法的 Example
為了說明controller組件是如何在angular中工作的,讓我們使用以下組件創(chuàng)建一個小應(yīng)用:
- 一個有兩個按鈕和一個簡單消息的template。
- 一個由名為”spice”的字符串屬性組成的model。
- 一個有兩個設(shè)置spice屬性的方法的controller。
在我們的模版里面的消息,包含一個到spice model的綁定,默認設(shè)置為”very”。根據(jù)被單擊按鈕,將spice model的值設(shè)置為”chili”或者” jalapeño”,消息會被數(shù)據(jù)綁定自動更新。
<!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被用作為我們的模版(隱式)創(chuàng)建scope,那個scope會稱為SpicyCtrl的參數(shù)。
- SpicyCtrl只是一個普通的javascript function。作為一個(隨意)的命名規(guī)則,名稱以大寫字母開頭,并以”Ctrl”或者”Controller”結(jié)尾。
- 對屬性賦值可以創(chuàng)建或者更新$scope的model。
- controller方法可以通過直接分配到$scope實現(xiàn)創(chuàng)建。(chiliSpicy方法)
- controller的兩個方法在template中都是可用的(在ng-controller屬性所在的元素以及其子元素中都有效)。
- 注意:之前版本的angular(1.0RC之前)允許我們使用this來代替$scope定義$scope的方法,但這里不再適用。在定義在scope上的方法中,this跟$scope是等價的(angular將this至為scope),但不是在我們的controller構(gòu)造函數(shù)中。
- 注意:之前版本的angular(1.0RC之前),會自動增加controller的prototype方法到scope中,但現(xiàn)在不會了。所有方法都需要人工加入到scope中。(印象中之前有一個guide,有用過這個。還沒更新-_-!)
controller方法可以帶參數(shù)的,正如下面例子所示:
<!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現(xiàn)在只定義了一個有一個參數(shù)”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>
注意我們?nèi)绾吻短?個ngController directive到模版中的。為了我們的視圖,這模版結(jié)構(gòu)將會導(dǎo)致4個scope被創(chuàng)建:
- 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,這里跟之前一樣,我還是沒理解。)
五、 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中創(chuàng)建與DOM里面相同的scope繼承關(guān)系。
<!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>
以上就是關(guān)于 AngularJs Understanding the Controller Component的資料整理,后續(xù)繼續(xù)補充相關(guān)資料,謝謝大家的支持!
相關(guān)文章
angular.js指令中的controller、compile與link函數(shù)的不同之處
最近一位大神問了我angular.js指令中的controller、compile與link函數(shù)的不同,想了想居然回答不出來,所以必須要深入的探究下,下面這篇文章主要介紹了關(guān)于angular.js指令中的controller、compile與link函數(shù)的不同之處,需要的朋友可以參考下。2017-05-05Angular4集成ng2-file-upload的上傳組件
本篇文章主要介紹了Angular4集成ng2-file-upload的上傳組件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03基于Angular中ng-controller父子級嵌套的相關(guān)屬性詳解
今天小編就為大家分享一篇基于Angular中ng-controller父子級嵌套的相關(guān)屬性詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10Angularjs material 實現(xiàn)搜索框功能
這篇文章主要介紹了Angularjs material 實現(xiàn)搜索框功能的相關(guān)資料,需要的朋友可以參考下2016-03-03Angular使用 ng-img-max 調(diào)整瀏覽器中的圖片的示例代碼
本篇文章主要介紹了Angular使用 ng-img-max 調(diào)整瀏覽器中的圖片的示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-08-08Angular7實現(xiàn)拖放Drag?Drop示例詳解
這篇文章主要介紹了Angular7實現(xiàn)拖放Drag?Drop示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12