一、使用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接口,有了它,所有扩展条控件之间就可以进行通信了。


Angular(三)的更多相关文章

  1. 三种Web前端框架比较与介绍--Vue, react, angular

    一.Angular 1.MVVM(Model)(View)(View-model): 2.模块化(Module)控制器(Contoller)依赖注入: 3.双向数据绑定:界面的操作能实时反映到数据,数 ...

  2. Web端主流框架,jquery、angular、react、vue

    不得不说,前端技术发展非常迅速,时不多久就有一个新的东西冒出来,并且迅速膨胀发展,让旁观者眼花缭乱,让开发者目眩神迷,但总体上来说,这波互联网大浪潮带动了前端技术的大发展,给曾经那些苦苦挣扎于DOM操 ...

  3. ajax——CORS跨域调用REST API 的常见问题以及前后端的设置

    RESTful架构是目前比较流行的一种互联网软件架构,在此架构之下的浏览器前端和手机端能共用后端接口. 但是涉及到js跨域调用接口总是很头疼,下边就跟着chrome的报错信息一起来解决一下. 假设:前 ...

  4. seajs加载angularjs

    angularjs是自动完成模块的控制的,而seajs加载模块是异步的,所以不做修改,直接seajs加载angularjs会出错.            在这里讲下自己的解决方法 一.需要把ng-ap ...

  5. 好的web前端是如何拿到30万年薪的?

    2018年前端开发不再像过去几年里新技术框架层出不穷,而是各种组件,模块,很多东西都有痕迹可寻,技术都在原来的基础上有了革新和沉淀. 前端招聘方面也在紧跟技术发展,大量“滥竽充数”的速成开发者开始失去 ...

  6. Angular 2 从0到1 (三)

    作者:王芃 wpcfan@gmail.com 第一节:Angular 2.0 从0到1 (一)第二节:Angular 2.0 从0到1 (二)第三节:Angular 2.0 从0到1 (三)第四节:A ...

  7. Angular.js学习笔记(三)

    一.过滤器 1.uppercase,lowercase 大小写转换{{ "lower cap string" | uppercase }} // 结果:LOWER CAP STRI ...

  8. angular 2+ 变化检测系列三(Zone.js在Angular中的应用)

    在系列一中,我们提到Zone.js,Zones是一种执行上下文,它允许我们设置钩子函数在我们的异步任务的开始位置和结束位置,Angular正是利用了这一特性从而实现了变更检测. Zones.js非常适 ...

  9. Angular 个人深究(三)【由Input&Output引起的】

    Angular 个人深究(三)[由Input&Output引起的] 注:最近项目在做别的事情,angular学习停滞了 1.Angular 中 @Input与@Output的使用 //test ...

随机推荐

  1. Redis ---------- 持久化(AOF)操作

    每小时做一次快照持久化 8:00 快照持久化 9:00 快照持久化 10:00  快照持久化  上帝想玩弄人类,突然停电,55万个key丢失了 11:00 快照持久化 解决方案: 8:00-9:00在 ...

  2. java的有用基础知识(2013-05-02-bd 写的日志迁移

    JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库.是java开发工具包 jre是java的运行环境(如果不做开发就不用安装jdk单独安装jre就可以运行java程序 ...

  3. 010---Django的模型层(2)

    确定模型关系: ''' Publish ---- Book 多对一:一个出版社对应多本书,在多的那张表添加关联字段 Book ---- Author 多对多:一个书对应多个作者,多个作者对应一本书 会 ...

  4. Lighting System Design UVA - 11400 动态规划

    题目:题目链接 思路:简单的动态规划问题,先把灯泡按照电压从小到大排序.设s[i]为前i种灯泡的总数量(即L值之和),d[i]为灯 泡1-i的最小开销,则d[i] = min{d[j] + (s[i] ...

  5. python正则表达式01--贪心算法和非贪心算法findall()

    import re st = 'asdfasxxixxdafqewxxlovexxsadawexxyouxxas' # . #点匹配除换行符外的任意字符 a0 = re.findall('xx.',s ...

  6. mysql学习第二天函数

    -- 1.绝对值 select abs(-1)from dual -- 2.求平方根select sqrt(6)from dual -- 3.圆周率select pi()from dual -- 4. ...

  7. 零基础学css第二天

    内边距与外边距: <!DOCTYPE html> <html> <head> <title></title> <style type= ...

  8. 使用dataframe解决spark TopN问题:分组、排序、取TopN和join相关问题

    package com.profile.mainimport org.apache.spark.sql.expressions.Windowimport org.apache.spark.sql.fu ...

  9. Enhacing the content with JavaScript

    What not to do :  In theory , you could use JavaScript to add important content to a web page. Howev ...

  10. 8,实例化Flask的参数 及 对app的配置

    Flask 是一个非常灵活且短小精干的web框架 , 那么灵活性从什么地方体现呢? 有一个神奇的东西叫 Flask配置 , 这个东西怎么用呢? 它能给我们带来怎么样的方便呢? 首先展示一下: from ...