AngularJs Understanding the Controller Component


Posted in Javascript onSeptember 02, 2016

在angular中,controller是一个javascript 函数(type/class),被用作扩展除了root scope在外的angular scope(https://3water.com/article/91749.htm)的实例。当我们或者angular通过scope.$new API(http://docs.angularjs.org/api/ng.$rootScope.Scope#$new)创建新的child scope时,有一个选项作为方法的参数传入controller(这里没看明白,只知道controller的第一个参数是一个新创建的scope,有绑定parent scope)。这将告诉angular需要联合controller和新的scope,并且扩展它的行为。

controller可以用作:

1.设置scope对象的初始状态。
2.增加行为到scope中。

一、 Setting up the initial state of a scope object(设置scope对象的初始状态)

通常,当我们创建应用的时候,我们需要为angular scope设置初始化状态。

angular将一个新的scope对象应用到controller构造函数(估计是作为参数传进去的意思),建立了初始的scope状态。这意味着angular从不创建controller类型实例(即不对controller的构造函数使用new操作符)。构造函数一直都应用于存在的scope对象。

我们通过创建model属性,建立了scope的初始状态。例如:

                function GreetingCtrl ($scope) {$scope.greeting = “Hola!”;}

“GreetingCtrl”这个controller创建了一个叫“greeting”的,可以被应用到模版中的model。

二、 Adding Behavior to a Scope Object(在scope object中增加行为)

在angular scope对象上的行为,是以scope方法属性的形式,供模版、视图使用。这行为(behavior)可以修改应用的model。

正如指引的model章节(https://3water.com/article/91777.htm)讨论的那样,任意对象(或者原始的类型)赋值到scope中,成为了model属性。任何附加到scope中的function,对于模版视图来说都是可用的,可以通过angular expression调用,也可以通过ng event handler directive调用(如ngClick)。

三、 Using Controllers Correctly

一般而言,controller不应该尝试做太多的事情。它应该仅仅包含单个视图所需要的业务逻辑(还有点没转过弯了,一直认为Controller就是个做转发的……)。

保持Controller的简单性,常见办法是抽出那些不属于controller的工作到service中,在controller通过依赖注入来使用这些service。这些东西会在向导的Dependency Injection Services章节中讨论。

不要在Controller中做以下的事情:

  1. 任何类型的DOM操作 - controller应该仅仅包含业务逻辑。DOM操作,即应用的表现逻辑,它的测试难度是众所周知的。将任何表现逻辑放到controller中,大大地影响了应用逻辑的可测试性。angular为了自动操作(更新)DOM,提供的数据绑定(http://docs.angularjs.org/guide/dev_guide.templates.databinding)。如果我们希望执行我们自定义的DOM操作,可以把表现逻辑抽取到directive(https://3water.com/article/91739.htm)中。
  2. Input formatting(输入格式化) - 使用angular form controls (https://3water.com/article/91744.htm)代替。
  3. Output filtering (输出格式化过滤) - 使用angular filters 代替。
  4. 执行无状态或有状态的、controller共享的代码 - 使用angular services 代替。
  5. 实例化或者管理其他组件的生命周期(例如创建一个服务实例)。 

四、 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中工作的,让我们使用以下组件创建一个小应用:

  1. 一个有两个按钮和一个简单消息的template。
  2. 一个由名为”spice”的字符串属性组成的model。
  3. 一个有两个设置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>

在上面例子中需要注意的东东:

  1. ngController directive被用作为我们的模版(隐式)创建scope,那个scope会称为SpicyCtrl的参数。
  2. SpicyCtrl只是一个普通的javascript function。作为一个(随意)的命名规则,名称以大写字母开头,并以”Ctrl”或者”Controller”结尾。
  3. 对属性赋值可以创建或者更新$scope的model。
  4. controller方法可以通过直接分配到$scope实现创建。(chiliSpicy方法)
  5. controller的两个方法在template中都是可用的(在ng-controller属性所在的元素以及其子元素中都有效)。
  6. 注意:之前版本的angular(1.0RC之前)允许我们使用this来代替$scope定义$scope的方法,但这里不再适用。在定义在scope上的方法中,this跟$scope是等价的(angular将this至为scope),但不是在我们的controller构造函数中。
  7. 注意:之前版本的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被创建:

  1. root scope。
  2. MainCtrl scope,包含timeOfDay和name model。
  3. ChildCtrl scope,覆盖了MainCtrl scope的name model,继承了timeOfDay model。
  4. 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中创建与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>

以上就是关于 AngularJs Understanding the Controller Component的资料整理,后续继续补充相关资料,谢谢大家的支持!

Javascript 相关文章推荐
HTTP状态代码以及定义(解释)
Feb 02 Javascript
将HTMLCollection/NodeList/伪数组转换成数组的实现方法
Jun 20 Javascript
Document:getElementsByName()使用方法及示例
Oct 28 Javascript
node.js中的http.request.end方法使用说明
Dec 10 Javascript
js实现常见的工具条效果
Mar 02 Javascript
jQuery实现右侧抽屉式在线客服功能
Dec 25 jQuery
vue.js动画中的js钩子函数的实现
Jul 06 Javascript
js删除数组中某几项的方法总结
Jan 16 Javascript
Vue开发之watch监听数组、对象、变量操作分析
Apr 25 Javascript
详解js中let与var声明变量的区别
Apr 05 Javascript
vue项目使用高德地图的定位及关键字搜索功能的实例代码(踩坑经验)
Mar 07 Javascript
JS实现鼠标移动拖尾
Dec 27 Javascript
JS 拼凑字符串的简单实例
Sep 02 #Javascript
AngularJs Understanding the Model Component
Sep 02 #Javascript
浅谈js中StringBuffer类的实现方法及使用
Sep 02 #Javascript
AngularJs Dependency Injection(DI,依赖注入)
Sep 02 #Javascript
js实现String.Fomat的实例代码
Sep 02 #Javascript
在Docker快速部署Node.js应用的详细步骤
Sep 02 #Javascript
CSS3 3D 技术手把手教你玩转
Sep 02 #Javascript
You might like
php图片添加水印例子
2016/07/20 PHP
PHP jQuery+Ajax结合写批量删除功能
2017/05/19 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
jQuery 注意事项 与原因分析
2009/04/24 Javascript
juqery 学习之五 文档处理 包裹、替换、删除、复制
2011/02/11 Javascript
javascript继承之为什么要继承
2012/11/10 Javascript
JS+CSS实现感应鼠标渐变显示DIV层的方法
2015/02/20 Javascript
jQuery Select下拉框操作小结(推荐)
2016/07/22 Javascript
深入理解js generator数据类型
2016/08/16 Javascript
js的OOP继承实现(必看篇)
2017/02/18 Javascript
node使用UEditor富文本编辑器的方法实例
2017/07/11 Javascript
JavaScript中Object值合并方法详解
2017/12/22 Javascript
vue组件实现弹出框点击显示隐藏效果
2020/10/26 Javascript
Vue中的v-for循环key属性注意事项小结
2018/08/12 Javascript
jquery获取select选中值的文本,并赋值给另一个输入框的方法
2018/08/21 jQuery
详解vue-property-decorator使用手册
2019/07/29 Javascript
JS+CSS+HTML实现“代码雨”类似黑客帝国文字下落效果
2020/03/17 Javascript
mpvue实现微信小程序快递单号查询代码
2020/04/03 Javascript
Python ORM框架SQLAlchemy学习笔记之安装和简单查询实例
2014/06/10 Python
常见的在Python中实现单例模式的三种方法
2015/04/08 Python
Python中isnumeric()方法的使用简介
2015/05/19 Python
Python面向对象编程基础解析(二)
2017/10/26 Python
将pandas.dataframe的数据写入到文件中的方法
2018/12/07 Python
详解Python_shutil模块
2019/03/15 Python
django中嵌套的try-except实例
2020/05/21 Python
高校毕业生登记表自我鉴定
2013/11/03 职场文书
求职信写作要突出重点
2014/01/01 职场文书
大学生自我鉴定评语
2014/01/27 职场文书
网络信息安全承诺书
2014/03/26 职场文书
活动总结报告范文
2014/05/04 职场文书
理想点亮人生演讲稿
2014/05/21 职场文书
教师节学生演讲稿
2014/09/03 职场文书
个人反四风对照检查材料思想汇报
2014/09/23 职场文书
个人师德师风自我剖析材料
2014/09/29 职场文书
商铺租房协议书范本
2014/12/04 职场文书
Vue的过滤器你真了解吗
2022/02/24 Vue.js