时隔一个星期,接着上一篇的angularjs应用骨架继续聊聊angularjs其他的其他的内容。

  区分UI和控制器的职责

  在应用控制器中有三种职责:

  1、为应用中模型设置初始状态

  2、通过$scope对象把数据模型和函数暴露给视图(UI模版)

  3、监视模型其余部分的变化,并采取相应的动作

  我这主要想说的是第三项的功能:为了让控制器保持小巧可控制的状态,我们的建议是,为视图中的每一块功能区域创建一个控制器。就是说如果你有一个菜单,就创建一个MenuController,如果你有一个面包屑导航就创建一个NavController,你可能已经明白,但是需要明确的是,控制器是绑定在DOM上的,这些片段就是它们需要管理的内容。有两种方式可以把控制器关联到DOM节点上,第一种是通过ng-controller属性来声明,另一种是通过路由把它绑定到一个动态加载的DOM模版片段上,这个模版叫做视图。

  如果你的UI带有一些非常复杂的区域,你可以创建嵌套的控制器,它们可以通过继承树结构来共享数据模型和函数,这样你就可以保持代码的简单和可维护性。嵌套控制器非常的简单,只需要把控制器设置到DOM元素内部嵌套的元素上即可,示例如下:

  

 <div ng-controller="ParentController">
<div ng-controller="ChildController"></div>
</div>

  虽然我们把这中方式叫做控制器嵌套,但真实的嵌套发生在$scope对象上。通过内部的原型继承机制,父控制器对象上的$scope会被传递给内部嵌套控制器的$scope.具体到上面的例子就是,ChildController上的$socpe可以访问到ParentController上的$scope对象上的所有属性(和函数)。

  利用$scope暴露模型和数据

  利用向控制器传递$scope对象的机制,可以把模型数据暴露给视图。在你的应用中可能还有其他的数据,但是只有通过$scope触及这些数据,angular才会把它当成数据模型的一部分。你可以把$scope当成一个上下文环境,它让数据模型上的变化变得可以观察。

  对于显式的创建$scope的属性我们已经看见过许多例子。例如:$scope.count=5。还可以通过模版间接的创建数据模型。你可以通过一下几种方式来实现这一点。

  1、通过表达式。既然表达式是在控制器中的$scope环境中执行的,而这个$scope与它们管理的元素有关,那么在表达式中设置属性和属性控制器中的$scope属性值是一样的 。也就是说,可以这样做:

  

 <button ng-click="count=3"> Set count to 3</button>
<div ng-controller="CountController">
<button ng-click="SetCount()">Set count to 3</button>
</div>
<script type="text/javascript">
function CountController($scope){
$scope.SetCount= function () {
$cope.count=3;
};
}
</script>

2、在表单输入项上使用ng-modal。与表达式类似,ng-model上指定模型参数同样工作在外层控制器内。唯一不同点在于,这样会在表单项和指定的模型之间建立双向绑定关系。

  使用$watch监控数据模型的变化

  在$scope内置函数中,用的最多的就是$watch函数了,当你的数据模型中某一部分发生变化时,$watch函数可以向你发出通知。你可以监控单个对象的属性,也可以监控需要经过计算的结果(函数),实际上只要是能够当作属性访问到,或者可以当作一个javascript函数被计算出来,就可以被$watch函数监控。它的函数签名是$watch(watchFn,watchAction,deetWatch).

  其中参数说明:

  watchFn:该参数是一个带有angular表达式或者函数的字符串,它会返回被监控in个的数据模型的当前值。这个表达式会被执行很多次,所以要保证它不会长生其他副作用。也就是说,要保证它被调用很多次而不后悔改变状态。

  watchAction:这是一个函数或者表达式,当watchFn发生变化时会被调用。如果是函数形式,它会接收到watchFn的新旧两个值,以及作用域对象的引用。其函数签名为function(newValue,oldValue,scope)。

  deepWatch:如果设置为true,会告诉angualr去检查被监控对象的每个值是否发生变化。那么运算负担就会比较重。

  示例:当用户添加到购物车中的价值超过美元的时候就会给美元的折扣:

  

 <!DOCTYPE html>
<html ng-app="MyApp">
<head>
<meta charset="UTF-8">
<title></title>
<script src="static/js/angular.js" type="text/javascript"></script>
<script src="static/app/controller/shopingcartcontroller.js" type="text/javascript"></script>
</head>
<body ng-controller="myController">
<div>
<h1>Your Shoping Cart</h1>
<div ng-repeat="item in items">
<span>{{item.title}}</span>
<input type="text" ng-model="item.quantity"/>
<span>{{item.quantity | currency}}</span>
<span>{{item.quantity * item.price | currency}}</span>
<button ng-click="remove($index)">remove</button>
</div> <div>total:{{totalCart()| currency}}</div>
<div>Discount:{{bill.discount | currency}}</div>
<div>Subtotal:{{ subtotal() | currency}}</div>
</div>
</body>
</html>
 /**
* Created by Administrator on 2015/6/12.
*/
var app=angular.module('MyApp',[]);
app.controller('myController', function ($scope) {
$scope.bill={};
$scope.items=[
{title:"羽毛球",quantity:8,price:3.99},
{title:"篮球",quantity:17,price:1.99},
{title:"足球",quantity:5,price:3.99}
]; $scope.remove=function(index){
$scope.items.splice(index,1);
} $scope.totalCart= function () {
var total=0;
for(var i= 0,len=$scope.items.length;i<len;i++){
total=total + $scope.items[i].price * $scope.items[i].quantity;
}
return total;
}; $scope.subtotal= function () {
return $scope.totalCart() - $scope.discount;
}; function calulateDdiscount(newvalue,oldvalue,scope){
$scope.bill.discount=newvalue > 100 ? 10 :0;
}; $scope.$watch($scope.totalCart,calulateDdiscount);
});

效果图:

$watch()中的性能注意事项

  虽然前面的例子能够正确的执行,但是却存在潜在的性能问题。如果你在totalCart()上打一个断点,你会看到,渲染页面时该函数被调用了6次。那么问题来了,为什么会是6次呢?让我们来分析一下:其中的三次我们能容易的追踪到:

  1、模版{{totalCart() | currency}}

  2、subtotal()函数

  3、$watch()函数

  然后angualr又把上诉过程重复一边,最终就是6次。当然有方案解决问题:一种是监控数组的变化,然后重新计算$scope属性中的总价、折扣和小计值。

  为了方便,我们把模版修改成这样:

  

        <div>total:{{bill.total| currency}}</div>
<div>Discount:{{bill.discount | currency}}</div>
<div>Subtotal:{{ bill.subtotal | currency}}</div>

  我们会监控items数组,当数组发生任何变化时,调用一个函数来计算总价,如下:

  

 <!DOCTYPE html>
<html ng-app="MyApp">
<head>
<meta charset="UTF-8">
<title></title>
<script src="static/js/angular.js" type="text/javascript"></script>
<script src="static/app/controller/shopingcartcontroller.js" type="text/javascript"></script>
</head>
<body ng-controller="myController">
<div>
<h1>Your Shoping Cart</h1>
<div ng-repeat="item in items">
<span>{{item.title}}</span>
<input type="text" ng-model="item.quantity"/>
<span>{{item.quantity | currency}}</span>
<span>{{item.quantity * item.price | currency}}</span>
<button ng-click="remove($index)">remove</button>
</div> <div>total:{{bill.totalCart| currency}}</div>
<div>Discount:{{bill.discount | currency}}</div>
<div>Subtotal:{{ bill.subtotal | currency}}</div>
</div>
</body>
</html>
 /**
* Created by Administrator on 2015/6/12.
*/
var app = angular.module('MyApp', []);
app.controller('myController', function ($scope) {
$scope.bill = {};
$scope.items = [
{title: "羽毛球", quantity: 8, price: 3.99},
{title: "篮球", quantity: 17, price: 1.99},
{title: "足球", quantity: 5, price: 3.99}
]; $scope.remove = function (index) {
$scope.items.splice(index, 1);
} var calulateTotal = function () {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.totalCart = total;
$scope.bill.discount = total > 100 ? 10 : 0;
$scope.bill.subtotal=total - $scope.bill.discount;
}; $scope.$watch("items", calulateTotal,true);
});

 效果图就不上了,和上面是一样的。但是你在calulateTotal()函数上打个断点,你会发现页面渲染时只调用了一次。这样在性能上就有了很大的提升。请注意,上面的代码在调用$watch函数时把items写成了一个字符串,那是因为$watch函数可以接受一个函数也可以接受一个字符串,如果把一个字符串传给函数,将会在被调用的$scope作用域中当作表达式来执行。但是,监控items数组,那么angular就要拷贝一份items数组用来比较操作。对于大型的items数组来说,如果angular每次显示页面只需要重新计算bill的值那么性能会好很多。

模版没有改变,控制器发生变化:

 /**
* Created by Administrator on 2015/6/12.
*/
var app = angular.module('MyApp', []);
app.controller('myController', function ($scope) {
$scope.bill = {};
$scope.items = [
{title: "羽毛球", quantity: 8, price: 3.99},
{title: "篮球", quantity: 17, price: 1.99},
{title: "足球", quantity: 5, price: 3.99}
]; $scope.remove = function (index) {
$scope.items.splice(index, 1);
} //var calulateTotal = function () {
// var total = 0;
// for (var i = 0, len = $scope.items.length; i < len; i++) {
// total = total + $scope.items[i].price * $scope.items[i].quantity;
// }
// $scope.bill.totalCart = total;
// $scope.bill.discount = total > 100 ? 10 : 0;
// $scope.bill.subtotal=total - $scope.bill.discount;
//}; $scope.$watch(function () {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.totalCart = total;
$scope.bill.discount = total > 100 ? 10 : 0;
$scope.bill.subtotal=total - $scope.bill.discount;
});
});

  

   监控多个东西

  如果想监控多个属性或对象,并且其中任何一个发生变化时就去执行一个函数。那该怎么做呢?其实在上面的例子中已经体现出来了。就是有三个商品,不管哪个商品的数量发生改变都会执行calulateTotal()函数,并且我在$watch函数的第三个参数设置成true。

  基本上解决上诉问题有两个选择:

  1、监控这些属性连接起来之后的值

  2、把它们放到一个数组和对象中,然后个deepWatch传递一个true值

  如:

  $scope.$watch('things.a + things.b',callMe());

  或者

  $scope.$watch('things',callMe(),true);

  

  

angularjs应用骨架(2)的更多相关文章

  1. angularjs应用骨架(3)

    好,继续上一章节我们继续聊聊angularjs骨架.开发任何一款优秀的应用都会面临一项非常困难的工作,那就是找到一种合适的方式方法把代码组织在合适的功能范围内.我们已经看过控制器的处理方式,它会提供一 ...

  2. angularjs应用骨架

    使用典型的类库时,你可以选择并使用你所喜欢的功能:而对于angularjs框架来说,必须把它看成一个完整的套件来使用,框架中的所有的东西都包含在里面,接下来将会介绍angular的基础模块,这样你就可 ...

  3. angularjs应用骨架(4)

    继续上一篇 继续了解angular其他内容. 与服务器交互 真正的应用需要和真实的服务器进行交互移动应用和新兴的Chrome桌面应用可能是例外.但是对于此外的所有应用来说,无论是想把数据持久化到云端还 ...

  4. 用angularjs开发下一代web应用(二):angularjs应用骨架(二)

    1.浅谈非入侵式JavaScript <div ng-click="doSomething()">...</div>这些指令和原来的事件处理器有下面不同之处 ...

  5. 【AngularJS】【02】AngularJS应用骨架

    ※文件引自OneDrive,有些人可能看不到

  6. AngularJs 基础(60分钟入门)

    AngularJS 是一个创建富客户端应用的JavaScript MVC框架.你仍然需要具有服务端后台,但大多数的用户交互逻辑将放到客户端上处理.它可以创建单页的应用程序,一个页面的应用仅仅需要HTM ...

  7. AngularJS 中文资料+工具+库+Demo 大搜集

    中文学习资料: 中文资料且成系统的就这么多,优酷上有个中文视频. http://www.cnblogs.com/lcllao/archive/2012/10/18/2728787.html   翻译的 ...

  8. 用AngularJS开发下一代Web应用 系列入门基础教程

    开篇介绍 AngularJS是什么东西?我觉得不用再描述了.可自行去充电一下.按照惯例,让我们先看看一个Hello World的开门简介吧. <!doctype html> <htm ...

  9. AngularJs 基础(60分钟入门) (转)

    AngularJs是一个不错的用于开发SPA应用(单页Web应用)的框架.单页Web应用(single page web application,SPA),就是只有一张Web页面的应用.浏览器一开始会 ...

随机推荐

  1. [SAM4N学习笔记]按键程序(查询方式)

    一.准备工作:      将上一节搭建的工程复制一份,命名为"5.key scanf".这一节主要讲如何使用SAM4N的GPIO输入功能,实现按键的输入. 二.程序编写:      ...

  2. 3 weekend110的shuffle机制 + mr程序的组件全貌

    前面,讲到了hadoop的序列化机制,mr程序开发,自定义排序,自定义分组. 有多少个reduce的并发任务数可以控制,但有多少个map的并发任务数还没 缓存,分组,排序,转发,这些都是mr的shuf ...

  3. Area - POJ 1654(求多边形面积)

    题目大意:从原点开始,1-4分别代表,向右下走,向右走,向右上走,向下走,5代表回到原点,6-9代表,向上走,向左下走,向左走,向左上走.求出最后的多边形面积. 分析:这个多边形面积很明显是不规则的, ...

  4. 探索AutoLayout的本质和解决一些问题

    最近频繁使用AutoLayout,记录下自己的一些发现和问题的解决(不是教程) 1.简介 Auto Layout 是苹果在 iOS 6中新引入的布局方式,旨在解决不同尺寸屏幕的适配问题. 屏幕适配工作 ...

  5. Scanner类的.next()和.hashNext()方法

    使用Scanner类可以很方便地便获取用户的键盘输入,Scanner是一个基于正则表达式的文本扫描器,它可以从文件.输入流 .字符串中解析出基本类型值和字符串值.Scanner类提供了多个构造器,不同 ...

  6. iOS 更好用的打Log方式-显示文件名、行数

    单纯的NSLog方式打出的Log没有显示打印语句所在的文件名和行数,下面这种做法会很实用: #ifdef DEBUG # define DLog(fmt, ...) NSLog((@"%s ...

  7. 20169210《Linux内核原理与分析》第十周作业

    第一部分:实验 进程的调度时机与进程的切换 操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已. 对于理解操作系统的工作机制 ...

  8. [C#] 常用工具类——直接在浏览器输出数据

    /// <summary> /// <para> </para> /// 常用工具类——直接在浏览器输出数据 /// <para> ---------- ...

  9. ListBox重绘

    .NET Framework 类库  ListBox.ItemHeight 属性 当 DrawMode 属性设置为 DrawMode.OwnerDrawFixed 时,所有项具有相同的高度.当 Dra ...

  10. JS获取和设置光标的位置

    <html> <head> <script language="javascript"> function getCursortPosition ...