一、使用Module(模块)组织依赖关系

模块提供了一种方法,可以用来组织应用中一块功能区域的依赖关系;同时还提供了一种机制,可以自动解析依赖关系(又叫做依赖注入)。一般来说,我们把这些叫做依赖服务,因为它们会负责为应用提供特殊的服务。

例如:如果购物站点中的一个控制器需要从服务器上获取一个商品列表,那么我们就需要某些对象——把它可以叫做Items——来处理从服务器端获取商品的工作。进而,Items对象就需要以某种方式与服务器上的数据库进行交互。

利用模块和模块内置的依赖注入功能,我们就可以把控制器写的更加简单,假设我们已经把Items对象定义成了一个服务,示例如下:

        function ShoppingController($scope,Items){
$scope.items=Items.query();
}

服务都是单例(单个示例)的对象,它们用来执行必要的任务,支撑应用的功能。angular内置了很多服务,例如$location服务,用来和浏览器的地址栏进行交互;$route服务,用来根据URL地址的变化切换视图;还有$http服务,用来和服务器进行交互。

当你需要在多个控制器之间进行交互和共享状态时,这些服务就是一种很好的机制。angular内置的服务以$开头。

以下3个函数可以用来创建一般的服务,它们的复杂度和功能不同。例如:provide、factory、service。

先看下针对Item的使用factory的例子,可以这样编写服务:

<!DOCTYPE html>
<html lang="en" ng-app='ShoppingModule'>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body ng-controller='ShoppingController'>
<h1>Shop</h1>
<table>
<tr ng-repeat="item in items">
<td>{{item.title}}</td>
<td>{{item.description}}</td>
<td>{{item.price|currency}}</td>
</tr>
</table>
<script src='angular-1.3.0.js'></script>
<script>
//创建一个模型用来支撑我们的购物视图
var shoppingModule=angular.module('ShoppingModule',[]);
//设置好服务工厂,用来创建我们的Items接口,以便访问服务端数据库
shoppingModule.factory('Items',function(){
var items={};
items.query=function(){
//在真实的应用中,我们会从服务端拉取这块数据
return [
{title:'paint',description:'pots full of paint',price:3.95},
{title:'polka',description:'dots with polka',price:2.95},
{title:'pebbles',description:'just little rocks',price:6.95}
];
};
console.log(items);
return items;
})
shoppingModule.controller('ShoppingController',['$scope','Items',function($scope,Items){
$scope.items=Items.query();
console.log($scope.items)
}]);
</script>
</body>
</html>

当angular创建 ShoppingController 时,它会把$scope对象和刚定义的Items服务作为参数传递进去。这一点是通过参数名匹配来实现的,也就是说,angular会查看我们的 ShoppingController 类的函数签名,然后就会发现它需要一个Items对象。既然我们已经把Items定义成了一个服务,那么angular当然知道去哪里找这个服务了。

以字符串的形式查找这些依赖关系的结果是,可以进行注入的那些函数(例如控制器的构造器)的参数是没有顺序的。

服务自身可以相互依赖,类似地,可以使用Module接口来定义模块之间的依赖关系。例如:如果你引入了model1和model2,那么应用的模块声明看起来可能会向下面这样:

var appMod = angular.module('app',['model1','model2']);

二、使用过滤器格式化数据

可不必受限于内置的过滤器,自己编写过滤器也非常简单。例如,我们需要为文字创建大写的字符串,代码如下:

<!DOCTYPE html>
<html lang="en" ng-app='MyModule'>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div ng-controller='myAppCtrl'>
<h1>{{pageHeading|titleCase}}</h1>
</div>
<script src='angular-1.3.0.js'></script>
<script>
var myModule=angular.module('MyModule',[]);
myModule.filter('titleCase',function(){
var titleCaseFilter=function(input){
var words=input.split(' ');
console.log(words);
for (var i=0;i<words.length;i++){
words[i]=words[i].charAt(0).toUpperCase()+words[i].slice(1);
}
return words.join(' ');
}
return titleCaseFilter;
})
myModule.controller('myAppCtrl',['$scope',function($scope){
$scope.pageHeading='behold the majesty of your page title';
}])
</script>
</body>
</html>

三、校验用户输入

angular自动为<form>元素增加了一些很好用的特性,使其更适合开发单页面应用。其中一个特性是,angular允许你为表单中的输入元素定义一个合法的状态,并且只有当所有元素都是合法状态时才允许提交表单。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body ng-controller='signUpCtrl'>
<h1>Sing Up</h1>
<form name="addUserForm">
<div ng-show='message'>{{message}}</div>
<div>First name:<input type="" name="firstName" ng-model='user.first' required></div>
<div>Last name:<input type="" name="lastname" ng-model='user.last' required></div>
<div><button ng-click='addUser()' ng-disabled='!addUserForm.$valid'>submit</button></div>
</form>
<script src='angular-1.3.0.js'></script>
<script>
var myModule=angular.module('myModule',[]);
myModule.controller('signUpCtrl',['$scope',function($scope){
$scope.message='';
$scope.addUser=function(){
$scope.message='thanks,'+$scope.user.first
}
}])
</script>
</body>
</html>

在控制器中,我们可以通过$valid属性获取表单的校验状态,当表单中的所有输入项都合法时,angular将会把这个属性设置为TRUE。我们可以利用这个属性来做很多很炫的事情,例如当表单没有输入完成时可以禁用submit按钮。

四、作用域

我们经常需要在指令中访问$scope对象,以便观察数据模型的值,当这些值发生变化时刷新UI。

  1. 绑定策略:

    @ 把当前属性作为字符串传递。

    = 绑定当前属性,它带有一个来自指令父scope的属性

    & 传递一个来自父scope的函数,稍后调用。

例如,我们要创建一个expender指令,它会显示一个很小的扩展条,点击的时候扩展条就会展开,显示额外的内容。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.expender{
border: 1px solid black;
width: 250px;
}
.expender >.title{
background: black;
color: white;
padding: .1em .3em;
cursor:pointer;
}
.expender > .body{
padding: .1em .3em
}
</style>
</head>
<body ng-controller='scopeCtrl'>
<expender class="expender" expender-title='title'>
{{text}}
</expender>
<script src='angular-1.3.0.js'></script>
<script>
var myModule=angular.module('myModule',[]);
myModule.controller('scopeCtrl',['$scope',function($scope){
$scope.title='click me to expend';
$scope.text='Hi there folks,i am the content' + 'that was hidder but is now shown.'
}]),
myModule.directive('expender',function(){
return{
restrict:'EA',
replace:true,
transclude:true,
scope:{title:'=expenderTitle'},
template:'<div>'+'<div class="title" ng-click="toggle()">{{title}}</div>'+'<div class="body" ng-show="showMe" ng-transclude></div>'+'</div>',
link:function(scope,element,attrs){
scope.showMe=false;
scope.toggle=function toggle(){
scope.showMe=!scope.showMe;
}
}
}
})
</script>
</body>
</html>

效果图:

标题的值(click me to expend)以及文本(hi there folks...)都来自外层scope。指令中的每一个选项分别代表什么:

  • restrict:EA 可以把这个指令当作一个元素或者一个属性进行调用。也就是说<expender...>...</expender>和<div expender...>...</div>是等价的
  • replace:TRUE 使用我们提供的模板来替换原来的元素
  • transclude: true 把原来元素中的内容移动到我们所提供的模板中
  • scope:{title:=expenderTitle} 创建scope的一个局部属性,名为title,他与父scope中的一个定义在expender-title中的属性绑定。这里,为了方便起见,我们把title重命名为expenderTitle。我们可以把scope编写成{expenderTitle:“=”},然后在模板中用expenderTitle来引用它。注意:这里的命名方式与指令自身一样采用了驼峰法则。
  • template:<div>+... 声明当前指令需要插入的模板。注意:我们使用了ng-click和ng-show来显示隐藏模板,使用ng-transclude来声明吧原来的内容放到那里。同时请注意,用来替换的模板会访问父scope,而不会访问封装它的指令所属的scope。
  • link:... 设置showMe模型来跟踪扩展条的打开、关闭状态,并定义toggle函数,当用户点击所在的div时调用这个函数。

注意,在使用@策略时,我们仍然可以通过双花括号插值语法把title绑定到控制器scope上:

<expender class="expender" expender-title={{title}}>
{{text}}
</expender>

五、操作DOM元素

link、compile传递的参数,都指向原始的DOM元素。如果加载了jQuery库,那么他们就会指向经过jQuery包装之后的元素。如果不使用jQuery,那么这些DOM元素都位于angular-native包装器jqLite中。jqLite是jQuery API的子集,在angular中我们需要用它来创建所有的东西。对于很多应用来说,只要使用这个轻量级API就可以实现所有你想做的事情了。

如果你需要直接访问原始的DOM元素,你就可以使用element[0]来访问对象中的第一个元素。

在angular文档中的angular.element()部分,可以看到目前支持的完整API列表,你可以使用angular.element()来创建包装在jqLite中的DOM元素。angular对象还带有addClass(),bind(),find(),toggleClass()等函数。当然,这些是jQuery中常用的核心函数,只是angular的实现代码更精致而已。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.expender{
border: 1px solid black;
width: 250px;
}
.expender >.title{
background: black;
color: white;
padding: .1em .3em;
cursor:pointer;
}
.expender > .body{
padding: .1em .3em
}
.closed{
display: none;
}
</style>
</head>
<body ng-controller='scopeCtrl'>
<expender class="expender" expender-title={{title}}>
{{text}}
</expender>
<script src='angular-1.3.0.js'></script>
<script>
var myModule=angular.module('myModule',[]);
myModule.controller('scopeCtrl',['$scope',function($scope){
$scope.title='click me to expend';
$scope.text='Hi there folks,i am the content' + 'that was hidder but is now shown.'
}]),
myModule.directive('expender',function(){
return{
restrict:'EA',
replace:true,
transclude:true,
scope:{title:'@expenderTitle'},
template:'<div>'+'<div class="title">{{title}}</div>'+'<div class="body closed" ng-transclude></div>'+'</div>',
link:function(scope,element,attrs){ var titleElement=angular.element(element.children().eq(0));
var bodyElement=angular.element(element.children().eq(1)); console.log(titleElement);
titleElement.bind('click',toggle);
function toggle() {
bodyElement.toggleClass('closed')
}
}
}
})
</script>
</body>
</html>

六、控制器

要实现需要彼此通信的嵌套指令,可以使用控制器。<menu>需要知道内部<menu-item>元素的信息,这样它才能够正确地显示和隐藏它们。同样地,<tab-set>需要知道内部<tab>元素的信息。

如前所示,为了创建能够在指令之间进行通信的接口,可以使用controller属性语法把控制器声明成指令的一部分:

  controller:function controllerConstructor($scope,$element,$attrs,$transclude) {

    }

controller函数是通过依赖注入的,所以这里所列出的参数列表都是可选的,可以按照其他顺序将其列出,当然这些参数都是具有某种潜在的用途。他们还是可用的服务子集。

通过require属性语法,其他指令可以把这个控制器传递给自己。require的完整形式如下:

require: '^?directiveName'
  • directiveName: 这个以驼峰法则命名的选项名称指定了控制器应该带有哪一条指令,所以,如果<my-menu-item>指令需要在它的父指令<my-menu>上找到一个控制器,就需要把它写成myMenu
  • ^ : 默认情况下,angular会从同一个元素上的命名指令中获取控制器。添加^符号的意思是,需要同时遍历DOM树去查找指令。对于<my-menu>,我们需要添加这个符号
  • ?: 如果没有找到所需要的控制器,angular会抛出一个异常,告诉问题所在。在字符串中添加一个?号的意思是说这个控制器是可选的,如果没有找到,不需要抛出异常。

例如,重写expender指令,让它可以用在一个“accordion”集合中。它会保证当你打开一个扩展条时,集合中的所有其他扩展条都会自动关闭掉。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.expender {
border: 1px solid black;
width: 250px;
} .expender > .title {
background: black;
color: white;
padding: .1em .3em;
cursor: pointer;
} .expender > .body {
padding: .1em .3em
} </style>
</head>
<body ng-controller='scopeCtrl'>
<accordion>
<expender class="expender" ng-repeat="expender in expenders" expender-title={{expender.title}}>
{{expender.text}}
</expender>
</accordion> <script src='angular-1.3.0.js'></script>
<script>
var myModule = angular.module('myModule', []);
myModule.controller('scopeCtrl', ['$scope', function ($scope) { $scope.expenders = [
{
title: 'click me to expend',
text: 'Hi there folks,i am the content' + 'that was hidder but is now shown.'
},
{
title: 'Click this',
text: 'I am even better text than you have seen previously'
},
{
title: 'No,click me',
text: 'I am text that should be seen before seeing other texts'
}
] }]);
myModule.directive('accordion', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
scope: {title: '@expenderTitle'},
template: '<div ng-transclude></div>',
controller: function () {
var expenders = []; this.gotOpened = function (selectedExpender) {
console.log(222);
angular.forEach(expenders, function (expender) {
if (selectedExpender != expender) {
expender.showMe = false;
}
})
}
this.addExpender = function (expender) {
expenders.push(expender);
console.log(expenders)
} }
}
});
myModule.directive('expender', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
require: '^?accordion',
scope: {title: '@expenderTitle'},
template: '<div>' +
'<div class="title" ng-click="toggle()">{{title}}</div>' +
'<div class="body" ng-show="showMe" ng-transclude></div>' +
'</div>',
link: function (scope, element, attrs, accordionController) {
//此处的accordionController是由require: '^?accordion'的controller决定的
scope.showMe = false;
accordionController.addExpender(scope);
console.log(accordionController); scope.toggle = function toggle() {
scope.showMe = !scope.showMe;
accordionController.gotOpened(scope);
}
}
}
})
</script>
</body>
</html>

效果图:

以上代码,通过编写accordion指令,做一些元素定位工作。我们把控制器的构造函数以及进行元素定位操作的方法添加到accordion指令中,定义了一个addExpander()函数,扩展条可以调用它来注册自身;还创建了一个可被扩展条调用的gotOpen()函数,通过它,accordion的控制器就知道把其他所有处于打开状态的扩展条都关闭。

在expender指令中,我们扩展它的时候要求accordion的控制器来自它的父元素,然后在合适的时候调用addExpender()和gotOpen()函数。

注意:accordion指令中的控制器创建了一个API接口,有了它,所有扩展条控件之间就可以进行通信了。


最新文章

  1. 查找数据库中重复的值的数据,having的使用,count(1),sum等聚会函数
  2. 关于JS
  3. eclipse的快捷操作(转)
  4. 【leetcode】Partition List
  5. 20160816_Redis一些资料
  6. HDU 4906 状态压缩dp
  7. PHP学习笔记 - 入门篇(3)
  8. bzoj2208
  9. Android中的Selector的用法
  10. 一致性哈希与java实现
  11. jacksons转换大小写处理
  12. SpringMVC传参
  13. openCV(四)---Canny边缘检测
  14. xcode磁盘大清理
  15. hbase参数配置和说明
  16. VisualStudio移动开发(C#、VB.NET)Smobiler开发平台——ImageTabBar控件的使用方式
  17. Vue系列之 =&gt; 使用webpack-dev-server工具实现自动打包编译
  18. stingray中使用angularjs
  19. crm
  20. 文章如何做伪原创 SEO大神教你几招做&quot;原创&quot;网站文章的心得

热门文章

  1. py2exe安装使用
  2. php-5.6.26源代码 - opcode执行
  3. 【TP5.1】HTML标签自动转义,导致CKEditor保存内容无法正常显示!
  4. TensorFlow验证码识别
  5. Android Config通用类来记录信息
  6. java练习题——字符串
  7. win32 signal
  8. python之列表/元组/字典/字符串
  9. python_ 运算符与分支结构
  10. 孤荷凌寒自学python第二十四天python类中隐藏的私有方法探秘