angularjs入门学习【指令篇】

时间:2023-11-25 15:11:44

一、首先我们来了解下指令API

属性 含义
restrict 申明标识符在模版中作为元素,属性,类,凝视或组合,怎样使用
priority 设置模版中相对于其它标识符的运行顺序
Template 指定一个字符串式的内嵌模版,假设你指定了模版是一个URL,那么是不会使用的
tempateUrl 指定URL载入的模版,假设你已经指定了内嵌的模版字符串,那么它不会使用的
Replace 假设为真,替换当前元素,假设是假或未指定,拼接到当前元素
Transclude 移动一个标识符的原始字节带你到一个新模版的位置
Scope 为这个标识符创建一个新的作用域,而不是继承父作用域
Controller 创建一个控制器通过标识符公开通信API 
Require 当前标识符须要另外一个标识符提供正确的函数功能
Link 通过代码改动目标DOM元素的实例,加入事件监听,建立数据绑定
Compile 通过标识符拷贝编程改动DOM模版

在这里,我们简单的了解下每一个属性的含义及其简单的作用,在后面我们将会通过一些励志来解释

接下来,我们来了解下指令定义对象

restrict:restrict属性同意为标识符指定声明样式,也就是说它能够作为元素名,属性,类或凝视。我们能够使用一个字符串来代表下表中的每一个标志,从而指定

一个或者多个声明样式

标志 样式 演示样例
E Element <my-menu title='products'></my-menu>
A Attribute <div my-menu='products'></div>
C Class <div class='my-menu:products'></div>
M Comment <!--directive:my-menu products-->

假设你希望标识符作为元素后者属性,你能够传递EA作为restrict的字符串

假设省略了restrict属性,默认就是A,具体请看angular文档

Priorities:为应用程序指定顺序,数值越大就越先执行,默认是为零,普通情况下无需设置优先级

Templates:在创建组件时,angular同意你在模版中替换和包装元素中的内容,假设你想创建例如以下的标签视图

angularjs入门学习【指令篇】

不是使用一串<div><ul><li><a>等元素来实现,而是能够通过自己定义创建标识符<tab-set><tab>,分别声明每一个页签的结构,可能假设下

<tab-set>
<tab title='Home'>
<p>Welcome home!</p>
</tab>
<tab title='Preferences'>
<!-- preferences UI goes here -->
</tab>
</tabset>

同一时候,你也可通过控制器为title和页签内容进行数据绑定,并用这样的方式做出菜单,手风琴,弹窗,对话框或其它应用需求

接下来,让我们来看看temp拉特或者templateUrl属性,指定替换DOM元素。在上表中,我们看到template能够用来设置模版内容的字符串,

templateUrl用于指定将被载入的server文件,正如接下来看到的演示样例,我们能够预缓存这些模版,一遍降低get请求数,提高性能

<html lang='en' ng-app='app'>
...
<body>
<hello></hello>
</body>
...

创建<hello></hello>标签替换<div>hi there</div>,replace设置为true同意拼接内容到元素上,设置replace成为true

var appModule = angular.module('app', []);
appModule.directive('hello', function() {
return {
restrict: 'E',
template: '<div>Hi there</div>', replace: true
};
});

在载入到浏览器后,我们会看到hi there,通过查看页面源代码,我们还是会看到<hello></hello>可是假设你检查生成代码(chrome,右击hi htere,选择检查元素)你会看到

<body>
<div>Hi there</div>
</body>

<hello></hello>已经被模版中的<div>替换。

而相对于使用template 输入html到字符串中不是非常有意义,一般我们都会使用templateUrl,进行设置适当的头部缓存

var appModule = angular.module('app', []);
appModule.directive('hello', function() {
return {
restrict: 'E',
templateUrl: 'helloTemplate.html', replace: true
};
});

在helloTemplate.html中,我们须要写入

<div> Hi there</div>

假设你使用chrome浏览器,同源策略会组织可能会导致遇到一个错误“Origin null is not allowed by Access-Controll-Allow-Origin”.这里你有两种可选方式

1.通过server载入应用

2.chrome 中设置一个标志,通过命令行‘chrome-allow-file-access-from-files’解决

然后通过templateUrl载入文件,会使用户等待知道载入后看到标识符,假设你希望第一次页面载入时就载入模版,你能够在script标签中让其作为页面的一部分,例如以下

<script type='text/ng-template' id='helloTemplateInline.html'>
<div>Hi there</div>
</script>

这里的id属性很重要,由于是URL键,angular用它来存储模版,你应该在标识符的templateUrl中使用id来指定插入那个模版

还有我们能够通过$http或其它几种机制载入模版,然后直接设置到angular所使用的$templateCache对象中,并可通过run函数调用它,使其在标识符执行之前让这个模版在

缓存中可用

var appModule = angular.module('app', []);
appModule.run(function($templateCache) {
$templateCache.put('helloTemplateCached.html', '<div>Hi there</div>');
});
appModule.directive('hello', function() {
return {
restrict: 'E',
templateUrl: 'helloTemplateCached.html', replace: true
};
});

Transclusion(嵌入包括)

通过transclude属性移动原始的内容到新模版中,当设置成为true时,标识符会删除原来的内容,并通过ng-transclude标识符使它又一次插入到模版中

採用transclusion方式改动演示样例:

appModule.directive('hello', function() {
return {
template: '<div>Hi there <span ng-transclude></span></div>', transclude: true
};
});

应用在:

<div hello>Bob</div>

我们会看到‘Hi there Bob.’

编译和链接函数

尽管插入模版是实用,可是在不论什么标识符真正有意义的工作发生在编译活着链接功能里

编译和链接的功能就是angular为引用创建实时视图的后两阶段,让我们来看下angular初始化过程的高层次视图,依照次序

【1】脚本载入:载入angular,查找ng-app标识符找到应用绑定

【2】编译阶段:在这一阶段,angular便利DOM标志模版中全部注冊的标志,对于每一个标识符,基于标识符规则(template,replace,transclude等等)改造DOM,然后

假设编译函数存在就调用它,结果一个编译的template函数,它会调用全部的标识符搜集的link韩素

【3】链接阶段:为了让视图动起来,angular为每一个标识符执行link函数,link函数通常在DOM或模型上创建监听器,这些监听器让视图和模型始终保持一致

因此到了编译阶段,它处理转换了模版,链接阶段,它处理了改动视图中的数据,沿着这些思路,标识符中表一功能和链接功能主要差别就是链接功能转换了模版自身,而连接功能在模型和视图上创建了动态链接,就是在第二阶段,作用域scpoes被附加到了编译过程的link功能上,通过数据绑定,标识符变活了

二、作用域

获取作用域scope的三种选择

1.标识符DOM元素中已经存在的作用域

2,创建一个继承封闭的控制器作用域的新作用域,以便读取结构树作用域的全部值。

3.独立作用于,从父类中不继承不论什么属性,当你须要隔离这个标识符的操作和父类作用域时,创建可从用的组建来使用这个选项

我们能够用例如以下语法类创建这些作用域配置

已有作用域 scope:false(假设没有指定,这就是默认值)
新作用域 scope:true
独立作用域 scope:{属性名次和绑定风格}

当你创建一个独立作用域时,默认情况下不能訪问父类作用域的模型,但我们能够通过指定须要的属性传递到标识符

注意:尽管独立作用域并没有继承模型属性,但他们仍然是父作用域的子节点,并$parent指向父类

我们能够通过标识符属性的键值对父类传递指定的属性给独立作用域,这里有三种可行的方式从父作用域传递数据,我们称这些传递数据方式叫做“绑定策略”,你能够为这个属性名称指定一个本地别名

没有别名的语法例如以下:

scope: { attributeName1: 'BINDING_STRATEGY',
attributeName2: 'BINDING_STRATEGY', …
}

用别名的格式例如以下:

scope: { attributeAlias: 'BINDING_STRATEGY' + 'templateAttributeName',

}
符号 意义
@ 传递字符串属性,你能够通过使用改写{{}}属性值从粉笔作用域中进行数据绑定
= 数据绑定属性在标识符父作用域的属性中
& 传递一个来自父作用域的函数,稍后调用

在这我们用一个详细样例上的变化来说明它们,比方我们想创建一个expander标识符,展示一个标题栏,当点击时扩展显示额外内容

关闭的时候

angularjs入门学习【指令篇】

打开的状态

angularjs入门学习【指令篇】

代码例如以下:

<div ng-controller='SomeController'>
<expander class='expander' expander-title='title'>
{{text}}
</expander>
</div>

控制器代码:

function SomeController($scope) {
$scope.title = 'Click me to expand';
$scope.text = 'Hi there folks, I am the content
+ 'that was hidden but is now shown.';
}

然后我们编写标识符

angular.module('expanderModule', [])
.directive('expander', function(){
return {
restrict: 'EA', replace: true,
transclude: true,
scope: { title:'=expanderTitle' }, 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;
}
}
}
});

样式:

.expander {
border: 1px solid black;
width: 250px;
}
.expander > .title {
background-color: black;
color: white; padding: .1em .3em; cursor: pointer;
}
.expander > .body {
padding: .1em .3em;
}

元素的功能

功能名  
Restrict:EA 描写叙述调用标识符为元素或属性,也就是<expander>..</expander>或

<div expander>...</div>
Replace:true 用提供的模版替换原来的元素
Transclude:true 移动原始元素内容到提供的模版中的另外一个地方
Scope:{title:=expanderTitle}} 创建一个叫title的本地作用域属性,它是用来数据绑定到expander-title

属性中声明的parent-scope属性,这里,为了方便expanderTitle冲命名

为title,因为expanderTitle在模版中,我们飙血作用域

scope:{expanderTitle:'='}来引用它,但在这个场景中,其它标识符也

有一个title属性,为了防止引起歧义,我们将其重命名
Template:<div>+ 为标识符提供即将插入的模版,注意,我们使用ng-click和ng-show

来展示或隐藏自身,ng-transclude来申明原始的内容将何去何从,

同一时候注意嵌入的内容能够訪问父作用域,而不是封闭标识符的

作用域
Link: 建立showMe模型,跟踪expander的打开和关闭状态,然后当用户点击title 

div时,调用定义的toggle函数

三、操作DOM元素

函数 描写叙述
Controller(name) 与控制器直接进行通信,这个函数返回绑定在元素上的控制器

假设这个元素不存在,它会遍历DOM,然后查找近期的父控制

器取代,假设參数名字是可选的,用于指定允许元素上其它

标识符名称,假设提供了,则返回标识符上的控制器,这个名字

应该使用驼峰式,也就是说用ngModel取代ng-model
Injector() 获取当前元素或者父元素的注入器,同意在这些模块中查找依赖
Scope() 返回当前元素或者近期父元素的作用域
inHeritedData() 和jquery的data()函数一样,inheritedData()以封闭的方式设置以及获取元素上的

数据,除了从当前元素获取数据,它会便利DOM查找

以下样例,我们不用ng-show和ng-click又一次实现之前expander演示样例

angular.module('expanderModule', [])
.directive('expander', function(){
return {
restrict: 'EA', replace: true, transclude: true,
scope: { title:'=expanderTitle' }, 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));
titleElement.bind('click', toggle);
function toggle() {
bodyElement.toggleClass('closed');
}
}
}
});

在上面我们从模版中移除了ng-click和ng-show标识符,然而,当用户点击expander标题时候,仍然运行预期的操作,我们从tittle元素上创建了一个jqLite元素,然后把toggle函数绑定到click事件上作为它的回调,在toggle函数,我们在expander body元素上调用toggleClass()

来加入或移除closed的类,我们会设置这个元素class设置成的displat:none

.closed {
display: none;
}