Angular指令概述
指令是一种执行的信号,一旦发布了这个指令,就要执行某项动作,HTML中的标签都可以理解为指令,但Angular中的指令要复杂很多,不但要创建元素,还要给元素附加某些特定的行为,因此,Angular中的指令是一个在特定DOM元素上执行的函数
指令定义的基础
在Angular中,定义指令需要调用directive
方法,该方法接收两个参数:
var app = angular.module('myapp', []);
app.directive(name, fn);
在定义的模块上使用directive
方法创建一个指令,name为指令的名称,fn是一个函数,它将返回一个对象,在这个对象中,定义了这个指令的全部行为
<div>
<ts-hello></ts-hello>
<div ts-hello></div>
<div class="ts-hello"></div>
<div data-ts-hello></div>
</div>
<script>
var app = angular.module('myapp', []);
app.directive('tsHello', function () {
return {
restrict: 'EAC',
template: '<h3>Hello, Angular!</h3>'
}
})
</script>
设置指令对象的基础属性
replace
:它的属性值是布尔类型,当该属性值为true时,表示用模板中的内容替换指令标记,否则则不替换,直接显示指令标记,默认为false
templateUrl
:它的属性值是一个URL地址,该地址指向一个模板页面
<div>
<ts-tplfile></ts-tplfile>
<ts-tplscript></ts-tplscript>
<ts-tplcache></ts-tplcache>
</div>
<script>
var app = angular.module('myapp', []);
app.run(function ($templateCache) {
$templateCache.put('cache', '<h3>模板内容来源于缓存</h3>')
});
app.directive('tsTplfile', function () {
return {
restrict: 'EAC',
templateUrl: 'tpl.html'
};
});
app.directive('tsTplscript', function () {
return {
restrict: 'EAC',
templateUrl: 'tpl',
replace: true
}
});
app.directive('tsTplcache', function () {
return {
restrict: 'EAC',
templateUrl: 'cache'
}
});
</script>
Angular指令对象的重要属性
指令对象中的transclude属性
transclude
属性的值是布尔值,默认为false,表示不开启,如果设置为true,则开启该属性,当开启后,则可以在模板中通过ng-transclude
方式替换指令元素中的内容
<ts-hello>
<div>我是自定义指令在HTML里的内容</div>
</ts-hello>
<script>
var app = angular.module('MyApp', []);
app.directive('tsHello', function () {
return {
restrict: 'EA',
template: '<div ng-transclude></div>',
replace: true,
transclude: true
}
});
</script>
指令对象中的link属性
指令对象中的link
属性的值是一个函数,在该函数中可以操控DOM元素对象,包括绑定元素的各类事件,定义事件触发时执行的内容
link: function(scope, element, attrs) {
// ...
}
link
函数包含3个主要的参数:
-
scope
参数表示指令所在的作用域 -
element
参数表示指令中的元素,改元素可以通过Angular内部封装的jqLite框架进行调用 -
attrs
参数表示指令元素的属性集合通过这个参数可以获取元素中的各类属性
<script type="text/ng-template" id="tpl">
<button>点击按钮</button>
</script>
<div>
<ts-tplscipt></ts-tplscipt>
<div>{{content}}</div>
</div>
<script>
var app = angular.module('myapp', []);
app.directive('tsTplscipt', function () {
return {
restrict: 'EAC',
templateUrl: 'tpl',
replace: true,
link: function (scope, element, attrs) {
element.bind('click', function () {
scope.$apply(function () {
scope.content = '这是点击后显示的内容';
})
attrs.$$element[0].disabled = true;
})
}
}
});
</script>
指令对象中的compile属性
该属性比起link
属性使用的很少,该属性返回一个函数或对象。当返回一个函数时,该函数名称为post
,而返回一个对象时,该对象中则包含两个名为pre
和post
方法函数,这两个函数名是系统提供的,不可修改。
<div ng-controller="myctrl">
<ts-a>
<ts-b>
{{tip}}
</ts-b>
</ts-a>
</div>
<script>
var app = angular.module('myapp', []);
app.controller('myctrl', function ($scope) {
$scope.tip = '跟踪compile执行过程';
});
app.directive('tsA', function () {
return {
restrict: 'EAC',
compile: function (tEle, tAttrs, trans) {
console.log('正在编译A指令');
return {
pre: function (scope, element, attrs) {
console.log('正在执行A指令中的pre函数');
},
post: function (scope, element, attr) {
console.log('正在执行A指令中的post函数');
}
}
}
}
});
app.directive('tsB', function () {
return {
restrict: 'EAC',
compile: function (tEle, tAttrs, trans) {
console.log('正在编译B指令');
return {
pre: function (scope, element, attrs) {
console.log('正在执行B指令中pre函数');
},
post: function (scope, element, attrs) {
console.log('正在执行B指令中的post函数');
}
}
}
}
});
</script>
Angular指令对象的scope属性
在Angular指令对象中,scope
属性使用频率很高,它的值包含两种类型,一种是布尔值,另一类是JSON对象
scope属性是布尔值
使用scope
属性自定义指令时,默认是布尔类型,初始值为false。在这种情况下,指令中的作用域就是指令元素所在的作用域。我们将指令中的作用域称为子作用域,把指令元素所在作用域称为父作用域,当scope
为false
时,子作用域和父作用域完全相同,一方变化,则另一方也会自动发生变化
当scope
为true
时,则表示子作用域是独立创建的,父作用域中内容的改变会影响子作用域,但子作用域的内容发生变化,并不会修改父作用域中的内容
<script type="text/ng-template" id="tpl">
<div>{{message}}</div>
<button ng-transclude></button>
</script>
<div>
<input type="text" ng-model="message" placeholder="请输入提示内容">
<ts-message>固定</ts-message>
</div>
<script>
var app = angular.module('myapp', []);
app.directive('tsMessage', function () {
return {
restrict: 'EAC',
templateUrl: 'tpl',
transclude: true,
scope: true,
link: function (scope, element, attrs) {
element.bind('click', function () {
scope.$apply(function () {
scope.message = '这是单击后的值';
})
})
}
}
})
</script>
第二个例子:
html:
<div ng-controller="myCtrl">
父亲: {{name}}
<input ng-model="name">
<div my-directive></div>
</div>
var app = angular.module('myApp', [])
app.controller('myCtrl', ['$scope', function ($scope) {
$scope.name = 'leifeng';
}]);
app.directive('myDirective', function () {
return {
restrict: 'EA',
scope: true, //父作用域发生改变,子作用域会变化,子作用域的变化不会影响父作用域
template: '<div>儿子: {{name}}<input ng-model="name"></div>'
}
});
scope属性是对象
除了将scope属性设置为布尔值之外,还可以设置成一个JSON对象,如果是对象,那么父作用域与子作用域是完全独立的,不存在任何关联
app.directive('myDirective', function () {
return {
resrict: 'EA',
scope: {}, //父作用域与子作用域相互不影响,改变任意一方都不会改变另一方
template: //....
}
})
这时,子作用域中如果需要添加属性,则可以通过link
函数,在scope
上添加,如果子作用域需要与调用父作用域的属性和方法,则需要在这个JSON对象中添加绑定策略
在JSON对象中添加的有3种绑定策略,分别是@
、=
和&
(1) @绑定
如果父作用域的属性内容修改了,子作用域对应的属性内容也会随之修改,而如果子作用域属性内容修改了,是不会影响父作用域对应的属性内容的。
html:
<div ng-controller="myCtrl">
父亲: {{name}}
<input ng-model="name">
<my-directive name="{{name}}"></my-directive>
</div>
javascript:
<script>
var app = angular.module('myApp', [])
app.controller('myCtrl', ['$scope', function ($scope) {
}]);
app.directive('myDirective', function () {
return {
restrict: 'EA',
scope: {
name: '@' //指令作用域中变量与父作用域中一致,直接使用@绑定
},
replace: true,
template: '<div>儿子: {{name}}<input ng-model="name"></div>'
}
});
</script>
(2) =绑定
=绑定
的功能是创建一个父作用域与子作用域可以同时共享的属性,即父作用域修改了该属性,子作用域也随之改变,反之亦然。
html:
<div ng-controller="myCtrl">
<input type="text" ng-model="color" placeholder="Enter a color">
{{color}}
<hello-world color='color'></hello-world>
<!--注意上面自定义指令里属性的写法-->
</div>
javascript:
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', ['$scope', function ($scope) {}]);
app.directive('helloWorld', function () {
return {
restrict: 'EA',
replace: true,
scope: {
color: '='
},
template: '<div style="background-color:{{color}}">Hello World<input type="text" ng-model="color"></div>'
}
});
</script>
(3) &绑定
&绑定
的功能可以在独立的子作用域中直接调用父作用域的方法,在调用时可以向函数传递参数。
<div ng-controller="myCtrl">
<input type="text" ng-model="name" placeholder="Eneter a color">
{{name}}
<hello-world saysomething999="say();" name="kaindy"></hello-world>
</div>
javascript:
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', ['$scope', function ($scope) {
$scope.say = function () {
alert('hello');
}
$scope.name = 'leifeng';
}]);
app.directive('helloWorld', function () {
return {
restrict: 'EA',
replace: true,
scope: {
saysomething: '&saysomething999',
name: '@'
},
template: '<button type="button" ng-bind="name" ng-init="saysomething();"></button>'
}
});
</script>
Angular指令对象的require和controller属性
require
和controller
两个属性常用于多个自定义指令元素嵌套时,即当一个子元素指令需要与父元素指令通信时,就需要使用这两个属性
require和controller属性的概念
require
属性在创建子元素指令时添加,它的属性值用于描述与父元素指令通信时的方式,如^
符号表示向外层寻找指定名称的指令,?
符号表示即使没有找到,也不会出现异常
require: "^?myDirective"
controller
属性值是一个构造函数,在创建父元素指令时添加,并可以在函数中创建多个属性或方法。在添加后,这些属性和方法都会被实例的对象所继承,而这个实例对象则是子元素指令中link
函数的第4个参数
也就是说,当在子元素指令中添加了require
属性,并通过属性值指定父元素指令的名称,那么就可以通过子元素指令中link
函数的第4个参数来访问父元素指令中controller
属性添加的方法。
controller: function () {
this.a = function (childDirective) {
// 方法a的函数体
}
}
-
controller
的属性值对应一个构造函数 -
this
代表父元素指令本身 -
a
表示构造函数中的一个任意的方法 -
childDirective
形参表示子元素指令中的scope
对象
html:
<div>
<!--父元素指令-->
<ts-parent>
<div>{{ptip}}</div>
<!--子元素指令-->
<ts-child>
<div>{{ctip}}</div>
</ts-child>
<button ng-click="click()">换位</button>
</ts-parent>
</div>
javascript:
<script>
var app = angular.module('myApp', []);
app.directive('tsParent', function () {
return {
restrict: 'EA',
controller: function ($scope, $compile, $http) {
this.addChild = function (c) {
$scope.ptip = '今天天气不错!';
$scope.click = function () {
$scope.tmp = $scope.ptip;
$scope.ptip = c.ctip;
c.ctip = $scope.tmp;
}
}
}
}
});
app.directive('tsChild', function () {
return {
restrict: 'EA',
// 与父元素指令tsParent进行通信
require: '^?tsParent',
// 第4个参数表示父元素指令本身,可以调用定义在其上的方法
link: function (scope, element, attrs, ctrl) {
scope.ctip = '气温正好18摄氏度';
ctrl.addChild(scope);
}
}
});
</script>