AngularJS基础教程
自定义指令
前言
在前面的章节中我们已经了解过了
AngularJS 中自带的指令,如:
ng-bind、
ng-repeat、
ng-view 等等。
AngularJS 还允许我们定义自己的指令。
我们的第一个自定义指令
在 module 上调用 directive() 方法创建我们的第一个自定义指令,这个自定义指令用来展示“用户数据”。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<!-- 在下面这个 div 中,有个自定义属性“user”,它就是我们的自定义指令 -->
<!-- 在一个元素中加入我们的“user”自定义属性后,在这个元素内部就会展示相关的内容 -->
<div
user></div>
<script type="text/javascript">
var app = angular.module('myApp', []);
// 在一个 module 上调用 directive() 方法来创建一个自定义指令。
// 第一个参数 'user' 就是这个指令的名字,
// 当在一个元素中写上 'user' 属性时,就是在使用这个自定义指令。
app.directive('
user', function () {
return {
// 这是我们的 'user' 指令所使用的模板,
// 这个模板将会被展示在使用了这个指令的元素中
template:'<div>用户名:Amanda</div>'
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
在浏览器中查看:
从运行结果可以看出,在使用了 user 属性的 div 中插入了 user 指令的 template。
如果将自定义指令的名字以“驼峰命名法”命名,如 myUser,则在使用时就需这样写:my-user:
<div my-user></div>
自定义指令中的数据绑定
自定义指令的模板中所绑定的数据可以来自 module 的 controller:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="myController">
<div
my-user></div>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
// 这里绑定的数据 name 来自 myController
template:'<div>用户名:{{name}}</div>'
};
});
myApp.controller('myController', function ($scope) {
$scope.name = 'Amanda';
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
在浏览器中查看:
也可以来自自定义指令自己的 controller,并且自定义指令自己的 controller 中的数据将覆盖 module 的 controller 中的数据:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="myController">
<div
my-user></div>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
// template 的值还可是一个 function,表示使用该 function 返回的值作为模板
template: function(){
// 这里绑定的数据 name 来自自定义指令的 controller
return '<div>用户名:{{name}}</div>';
},
controller: function ($scope) {
$scope.name = 'CYF';
}
};
});
myApp.controller('myController', function ($scope) {
$scope.name = 'Amanda';
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
在浏览器中查看:
使用外部模板文件
与“路由”的配置一样,自定义指令的模板也可以是一个单独的模板文件,只需将 template 改为 templateUrl 即可,并将 templateUrl 的值设为一个模板文件的路径:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<div
my-user></div>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
// 使用外部模板文件
templateUrl:'tmp0.html'
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
在当前页面的同级目录下创建一个名为 tmp0.html 的模板文件:
<div>这是来自tmp0.html的内容</div>
templateUrl 的值也可以是一个 function:
templateUrl: function(){
return 'tmp0.html';
}
还可依据属性值的不同使用不同的模板文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<div
my-user tmp="0"></div>
<div
my-user tmp="1"></div>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
// 可从第二个参数 attr 中获得元素的属性的值
templateUrl:function(ele, attr){
return 'tmp' + attr.tmp + '.html';
}
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
需要在当前文件的同级目录下新增一个名为 tmp1.html 的模板文件:
<div>这是来自tmp1.html的内容</div>
在浏览器中查看:
元素指令
在前面的例子中自定义的指令都是“属性指令”,若要自定义“元素指令”,则只需在配置中加入 restrict 即可:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<my-user></my-user>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
// 如果 restrict 的值为 'E',则表示这是一个“元素指令”,
// 默认为 'A',表示这是一个“属性指令”
restrict: 'E',
template: '这是自定义指令'
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
别名
先来看一个这样的例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="myController">
<my-user></my-user>
<br/>
<my-user></my-user>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('myUser', function () {
return {
restrict: 'E',
template: '名字:{{user.name}}; 年龄:{{user.age}}'
};
});
app.controller('myController', function($scope){
$scope.user = {name:'Amanda', age:'30'};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
在浏览器中查看:
两个 <my-user></my-user> 内显示的内容是完全一样的,因为它们绑定了同一个数据。
如果我们需要给它们分别绑定不同的数据呢?在上面这个例子中的写法在同一个 controller 内是无法做到的。要做到这一点,可以为自定义指令中的数据配置一个“别名”
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="myController">
<!-- 这个指令的模板内绑定的 user 数据是 $scope 中的 amanda -->
<my-user
userdata="amanda"></my-user>
<!-- 这个指令的模板内绑定的 user 数据是 $scope 中的 cyf -->
<my-user
userdata="cyf"></my-user>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('myUser', function () {
return {
restrict: 'E',
scope: {
// 在使用该自定义指令时,属性 userdata 指定的值即为该指令内 user 数据的别名。
// 如果写为 user:'=',则与写 user:'=user' 相同
user: '=userdata'
},
template: '名字:{{user.name}}; 年龄:{{user.age}}'
};
});
app.controller('myController', function($scope){
$scope.amanda = {name:'Amanda', age:'30'};
$scope.cyf = {name:'CYF', age:'30'};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
包含元素
如果使用自定义指令时,在自定义指令内包含了其它元素,如:
<my-user>
<div>包含元素</div>
</my-user>
按之前的写法,my-user 元素内包含的元素不会被显示。因为 my-user 内应该显示模板的内容,那么 my-user 元素内包含的元素应该显示在哪呢?这就需要用到 transclude:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<my-user>
<div>包含元素</div>
</my-user>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
restrict: 'E',
transclude: true,
template: '这是自定义指令<div ng-transclude></div>' // 包含的元素将显示在拥有 ng-transclue 属性的元素内
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
DOM操作
还在上面这个例子,如果希望对包含元素内的某个元素进行操作呢?例如:
<my-user>
<div>包含元素<span></span></div>
</my-user>
如果希望设置 my-user 内的 span 中的内容呢?
如果设置 myUser 指令的 controller 为:
controller: function($element){
// $element 是当前指令元素的jqLite对象,如果使用了JQuery,则为JQuery对象
$element.find('span').html('SPAN的内容');
}
运行之后,会发现为 span 设置的内容并没有成功。
这是因为在执行 controller 的时候,那个 span 还未包含在 $element 内。
应该在 link 内进行此操作。link 在 controller 之后执行,它将在模板被克隆之后执行。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<my-user>
<div>包含元素<span></span></div>
</my-user>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myUser', function () {
return {
restrict: 'E',
transclude: true,
template: '这是自定义指令<div ng-transclude></div>',
// 第一个参数 scope 为当前指令的 $scope
// 第二个参数 element 为当前指令的元素的jqLite对象,如果有使用JQuery,则为JQuery对象
link: function(scope, element){
element.find('span').html('SPAN的内容');
}
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
嵌套
在自定义的指令内还可嵌套其它的自定义指令。以下面这个“选项卡”自定义指令为例,注意嵌套指令之间是如何通信的:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>自定义指令</title>
<script src="js/angular.min.js"></script>
</head>
<body>
<my-tabs>
<my-tab title="选项一">这是选项卡一</my-tab>
<my-tab title="选项二">这是选项卡二</my-tab>
</my-tabs>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.directive('
myTabs', function () {
return {
restrict: 'E',
transclude: true,
template: '<div><button ng-repeat="tab in tabs" ng-click="select(tab)">{{tab.title}}</button></div><div ng-transclude></div>',
controller: function($scope){
// $scope.tabs 用来存放所有子指令的 scope,以方便对其操作
$scope.tabs = [];
// 注意:这里用的是 this,以方便在子指令中调用
this.addTab = function (tab) {
$scope.tabs.push(tab);
if ($scope.tabs.length == 1) {
$scope.select(tab);
}
};
// 该方法用来设置哪个子指令处于“选择状态”
$scope.select = function (tab) {
tab = tab || this.tab;
angular.forEach($scope.tabs, function (item) {
item.selected = item == tab;
});
};
}
};
});
app.directive('
myTab', function () {
return {
restrict: 'E',
// 除非找到指定的 controller,否则将抛出一个异常
// 前缀 ^^ 表示在其父级中查找
// 前缀 ^ 表示在其父级或自己中查找
// 没有前缀表示自己中查找
require: '^^myTabs',
transclude: true,
scope: {
title:'@' // scope.title 将与 title 属性关联起来
},
template: '<div ng-show="selected"><h3>{{title}}</h3><div ng-transclude></div></div>',
link: function(scope, element, attrs, tabsCtrl){
// 每个 my-tab 都调用父 controller 的 addTab 方法,
// 将自己的 scope 传过去,以方便在父 controller 中操作当前指令
tabsCtrl.addTab(scope);
}
};
});
angular.bootstrap(document, ['myApp']);
</script>
</body>
</html>
最后,来个广告
若你觉得此文不错,请分享,若认为尚需改进,请点讚。
结束语