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 相关文章推荐
ASP.NET jQuery 实例3 (在TextBox里面阻止复制、剪切和粘贴事件)
Jan 13 Javascript
自己编写的类似JS的trim方法
Oct 09 Javascript
javascript对下拉列表框(select)的操作实例讲解
Nov 29 Javascript
js智能获取浏览器版本UA信息的方法
Aug 08 Javascript
canvas实现图像截取功能
Feb 06 Javascript
微信小程序开发入门基础教程
Apr 19 Javascript
windows下vue-cli及webpack搭建安装环境
Apr 25 Javascript
响应式框架Bootstrap栅格系统的实例
Dec 19 Javascript
详解React-Router中Url参数改变页面不刷新的解决办法
May 08 Javascript
JavaScript实现图片上传并预览并提交ajax
Sep 30 Javascript
js+html实现点名系统功能
Nov 05 Javascript
原生javascript制作贪吃蛇小游戏的方法分析
Feb 26 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
法兰绒滤网冲泡
2021/03/03 冲泡冲煮
CodeIgniter实现更改view文件夹路径的方法
2014/07/04 PHP
php中PDO方式实现数据库的增删改查
2015/05/17 PHP
浅析PHP关键词替换的类(避免重复替换,保留与还原原始链接)
2015/09/22 PHP
PHP魔术方法以及关于独立实例与相连实例的全面讲解
2016/10/18 PHP
JQuery 学习笔记01 JQuery初接触
2010/05/06 Javascript
简略的前端架构心得&amp;&amp;基于editor为例子的编码小技巧
2010/11/25 Javascript
jQuery弹出层插件Lightbox_me使用指南
2015/04/21 Javascript
详解JavaScript编程中的数组结构
2015/10/24 Javascript
jQuery实现复选框的全选和反选
2017/02/02 Javascript
NodeJs使用Mysql模块实现事务处理实例
2017/05/31 NodeJs
vue3.0 CLI - 3.2 路由的初级使用教程
2018/09/20 Javascript
玩转Koa之核心原理分析
2018/12/29 Javascript
用原生JS实现爱奇艺首页导航栏代码实例
2019/09/19 Javascript
js实现省级联动(数据结构优化)
2020/07/17 Javascript
Vue自定义全局弹窗组件操作
2020/08/11 Javascript
Python操作Mysql实例代码教程在线版(查询手册)
2013/02/18 Python
PyQt 实现使窗口中的元素跟随窗口大小的变化而变化
2019/06/18 Python
django数据关系一对多、多对多模型、自关联的建立
2019/07/24 Python
Python assert语句的简单使用示例
2019/07/28 Python
windows下的pycharm安装及其设置中文菜单
2020/04/23 Python
阿巴庭院:Abba Patio
2019/06/18 全球购物
单位在职证明范本
2014/01/09 职场文书
保安部任务及岗位职责
2014/02/25 职场文书
餐饮采购员岗位职责
2014/03/15 职场文书
报关报检委托书
2014/04/08 职场文书
环保专项行动方案
2014/05/12 职场文书
企业承诺书怎么写
2014/05/24 职场文书
投标人廉洁自律承诺书
2014/05/26 职场文书
企业口号大全
2014/06/12 职场文书
深度好文:50条没人告诉你的人生经验,句句精辟
2019/08/22 职场文书
新手必备之MySQL msi版本下载安装图文详细教程
2021/05/21 MySQL
HTML+CSS 实现顶部导航栏菜单制作
2021/06/03 HTML / CSS
苹果电脑mac os中货币符号快捷输入
2022/02/17 杂记
Oracle中DBLink的详细介绍
2022/04/29 Oracle
Win Server2016远程桌面如何允许多用户同时登录
2022/06/10 Servers