指令是Angular1.X中比较复杂的内容,自定义指令能实现许多操作DOM元素的功能。下面就介绍七个简单场景下的自定义指令。
1.模拟ng-bind-html指令
思路:可以自定义一个属性指令(A),属性指令的值即需要替换的html文本,利用指令的link可以给带有该指令的元素添加经过$compile编译后($compile(XXX)(scope))的html文本。
结果截图:
代码:
<!doctype html>
<html ng-app="MyApp">
<head>
<meta charset="UTF-8">
<title>指令实现ng-bind-html</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<!-- 实现:手动模拟ng-bind-html指令 -->
<!-- 利用link及compile -->
<div ng-controller="myCtrl" bind-html="string">
</div>
</body>
<script>
var myApp = angular.module('MyApp', []);
myApp.controller('myCtrl', function($scope) {
$scope.string = '<button type="button" class="btn btn-primary" ng-click="btnFunc()">测试html按钮</button>';
});
myApp.directive('bindHtml', function($compile) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
if (scope.string) {
scope.$watch(attrs.bindHtml, function() {
/* 注意此处需要编译后的html代码,否则只会添加文本,无法添加绑定的事件 */
elem.append($compile(scope.string)(scope));
});
/* 添加的点击事件 */
scope.btnFunc = function() {
console.log('121');
};
}
}
};
});
</script>
</html>
注:append的代码需要编译后的html代码,否则只会添加文本,无法添加绑定的事件!并且在此处使用innerHTML也不会
2.给指令绑定不同的鼠标悬浮事件
场景描述:两行“数据加载中…”有不同的悬浮事件
思路:可以自定义一个指令,包含点击事件的属性,在子控制器中定义点击事件。
结果截图:
代码:
<!doctype html>
<html ng-app="MyApp">
<head>
<meta charset="UTF-8">
<title>Directive</title>
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<!-- 实现:给指令绑定不同的鼠标悬浮事件 -->
<!-- 相同指令在其自己的controller中,可以通过设置属性值(attr)传递不同的函数然后绑定到指令元素上 -->
<div ng-controller="MyCtrl1">
<loader howToLoad="loadFunc1()">数据加载中...</loader>
</div>
<div ng-controller="MyCtrl2">
<loader howToLoad="loadFunc2()">数据加载中...</loader>
</div>
<script type="text/javascript">
var myApp = angular.module('MyApp', []);
myApp.controller('MyCtrl1', ['$scope', function($scope) {
$scope.loadFunc1 = function() {
console.log('加载数据1...');
};
}]);
myApp.controller('MyCtrl2', ['$scope', function($scope) {
$scope.loadFunc2 = function() {
console.log('加载数据2...');
}
}]);
myApp.directive('loader', function() {
return {
restrict: "AE",
link: function(scope, element, attr) {
element.bind('mouseenter', function(event) {
//scope.loadData();
// scope.$apply("loadData()");
// 回调函数需要用到$apply进行页面的重绘
// 注意这里的坑,howToLoad会被转换成小写的howtoload
scope.$apply(attr.howtoload);
});
}
}
});
</script>
</body>
</html>
注:
1. 给指令元素绑定事件时用到的回调函数,回调函数里面需要用到$apply(func)进行页面重绘,从而实现指令事件的绑定。
2. 在link中使用属性attr时,需要注意只能小写,因为属性中的大写字母会被自动转换成小写。
3.指令绑定不同的鼠标悬浮事件(控制台打印出每个指令具有的属性)
场景描述:三个超人具有不同的超能力,要求悬浮在不同的超人文字上会在控制台打印出该超人具有的超能力。
思路:可以自定义一个Superman指令(E),该指令在其控制器中定义一个作用域变量——该超人具有的属性,并为该控制器添加几个增加特定超能力的函数;然后为每一个超能力自定义一个指令(A),可通过require建立与Superman指令的依赖关系,取得对Superman指令控制器的“使用权”,然后通过调用Superman指令控制器中操作超能力的方法去编辑Superman指令作用域中定义的超能力。
结果截图:
代码:
<!doctype html>
<html ng-app="MyApp">
<head>
<meta charset="UTF-8">
<title>Directive</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<!-- 实现:指令绑定不同的鼠标悬浮事件(控制台打印出每个指令具有的属性) -->
<!-- require及link中父Controller的利用 -->
<superman strength>超人1</superman>
<superman strength speed>超人2</superman>
<superman strength speed light>超人3</superman>
<script type="text/javascript">
var myApp = angular.module('MyApp', []);
myApp.directive('superman', function() {
return {
scope: {}, //独立Scope
restrict: 'E',
controller: function($scope) {
$scope.abilities = [];
this.addStrength = function() {
$scope.abilities.push('strength');
};
this.addSpeed = function() {
$scope.abilities.push('speed');
};
this.addLight = function() {
$scope.abilities.push('light');
};
},
link: function(scope, element, attrs) {
element.addClass("btn btn-primary");
element.bind('mouseenter', function(event) {
console.log(scope.abilities);
});
}
};
});
myApp.directive('strength', function() {
return {
restrict: 'A',
require: '^superman',
link: function(scope, element, attrs, supermanCtrl) {
supermanCtrl.addStrength();
}
};
});
myApp.directive('speed', function() {
return {
restrict: 'A',
require: '^superman',
link: function(scope, element, attrs, supermanCtrl) {
supermanCtrl.addSpeed();
}
};
});
myApp.directive('light', function() {
return {
restrict: 'A',
require: '^superman',
link: function(scope,
element, attrs, supermanCtrl) {
supermanCtrl.addLight();
}
};
});
</script>
</body>
</html>
4.根据选择的tab切换不同的内容
思路:利用ng-repeat指令生成基本骨架,然后利用ng-show控制内容快的显示。具体实现中给每个tab添加点击事件,保存点击的索引,利用$index与选中索引是否相等控制ng-show的值来实现切换效果。
结果截图:
代码:
<!DOCTYPE HTML>
<html ng-app="myApp">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Tab选项卡实例</title>
<style type="text/css">
.active {
background-color: rgb(65, 150, 187) !important;
color: #FFF !important;
}
</style>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<!-- 实现:根据选择的tab切换不同的内容 -->
<!-- 利用ng-repeat中的$index -->
<div ng-controller="myController">
<my-tab my-id="div1" my-data="sports"></my-tab>
<my-tab my-id="div2" my-data="time"></my-tab>
<my-tab my-id="div3" my-data="singer"></my-tab>
</div>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.controller('myController', ['$scope', function($scope) {
$scope.sports = [{
title: '篮球',
content: '111111111'
}, {
title: '足球',
content: '222222222'
}, {
title: '排球',
content: '333333333'
}];
$scope.time = [{
title: '上午',
content: '444444444'
}, {
title: '中午',
content: '555555555'
}];
$scope.singer = [{
title: '周杰伦',
content: '红尘客栈、牛仔很忙、给我一首歌的时间、听妈妈的话'
}, {
title: '陈奕迅',
content: '十年、K歌之王、浮夸'
}, {
title: '林俊杰',
content: '被风吹过的夏天、江南、一千年以后'
}];
}]);
myApp.directive('myTab', function() {
return {
restrict: "E",
replace: true,
scope: {
myId: '@',
myData: '='
},
template: '<div id="myId" ng-init="activeTab=0"><input type="button" ng-repeat="item in myData" ng-value="item.title" ng-class={"active":activeTab==$index} class="btn btn-default btn-sm" ng-click="toggleTab($index)">' +
'<div style="display:block" ng-repeat="item in myData" ng-show="activeTab==$index">{{item.content}}</div>' +
'</div>',
link: function(scope, elem, attrs) {
scope.toggleTab = function(index) {
scope.activeTab = index;
}
}
};
});
</script>
</body>
</html>
注:上面代码中自定义指令中的scope代表该指令拥有自己独立的作用域,这样别的指令就无法访问该指令的作用域,但利用scope可以直接获取元素中的属性值及函数等。scope给予一个对象时,表示执行绑定策略,可在template上调用这些数据。
a):使用@符号,表示解析普通字符串,说白了就是你写什麽就是什麽。
b):使用=符号,表示解析数据。
c):使用&符号,表示这绑定一个函数。
5.点击收起/显示内容
思路:这个实现起来比较简单,就是利用ng-show的值根据点击不断取反实现切换的效果。
结果截图:
代码:
<!DOCTYPE HTML>
<html ng-app="myApp">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>切换Toggle</title>
<style type="text/css">
.title {
background-color: #000;
width: 200px;
color: #FFF;
}
.content {
background-color: #FFF;
color: #000;
}
</style>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<!-- 点击收起/显示内容 -->
<div ng-controller="myCtrl">
<my-tab></my-tab>
</div>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.directive('myTab', function() {
return {
restrict: 'EA',
replace: true,
template: '<div>' +
'<div class="title" ng-click="clickTitle()">{{title}}</div>' +
'<div class="content" ng-show="isShow">{{text}}</div>' +
'</div>',
link: function(scope, elem, attr) {
scope.clickTitle = function() {
scope.isShow = !scope.isShow;
};
}
};
});
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.title = "点击展开";
$scope.text = "这里是内部的内容";
$scope.isShow = false;
}]);
</script>
</body>
</html>
6.多个tab收起/显示内容
思路:这个是生一个例子的进阶版,难点就是如何分别控制不同的内容框的显示与隐藏,本例采用的方法是在指令中初始化内容框ng-show的状态,将其保存在一个数组中,通过对数组的设置与取值实现多个tab的切换效果。
结果截图:
代码:
<!DOCTYPE HTML>
<html ng-app="myApp">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>切换Tab&&toggle</title>
<style type="text/css">
.title {
background-color: #000;
width: 200px;
color: #FFF;
}
.content {
background-color: #FFF;
color: #000;
}
</style>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<div ng-controller="myCtrl">
<!-- 复杂一点,实现:多个收起/显示内容 -->
<my-tab></my-tab>
</div>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.directive('myTab', function() {
return {
restrict: 'EA',
replace: true,
template: '<div ng-repeat="item in content">' +
'<div class="title" ng-click="clickTitle($index)">{{item.title}}</div>' +
'<div class="content" ng-show="isShow[$index]">{{item.text}}</div>' +
'</div>',
link: function(scope, elem, attr) {
var length = scope.content.length;
scope.isShow = [];
for (var i = 0; i < length; i++) {
scope.isShow.push(false);
}
scope.clickTitle = function(index) {
scope.isShow[index] = !scope.isShow[index];
};
}
};
});
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.content = [{
"title": "标题1",
"text": "内容1"
}, {
"title": "标题2",
"text": "内容2"
}];
}]);
</script>
</body>
</html>
7.实现自定义指令间的互相交互
思路:通过require、transclude之间配合实现,具体就不在赘述了,请看代码。不过注意有个坑,如果在父组件里定义了scope,则无法再子组件中访问父组件的作用域了,这是因为由于scope的定义使得父组件拥有了自己独立的作用域,无法被访问,注意啊注意啊!
结果截图:
代码:
<!DOCTYPE HTML>
<html ng-app="myApp">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>自定义指令间的互相交互</title>
<script src="https://cdn.bootcss.com/angular.js/1.3.0/angular.min.js"></script>
</head>
<body>
<!-- 父子组件之间的交互 -->
<div>
<my-outer my-content="huxiya">
<my-inner></my-inner>
</my-outer>
</div>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.directive('myOuter', function() {
return {
restrict: 'E',
replace: true,
transclude: true, //允许自定义指令的嵌套,通过ng-transclude指定嵌套的范围
scope: {
myContent: '@'
},
controller: function($scope) {
$scope.text = "Angular";
this.name = $scope.myContent;
},
template: '<div><h1>Hello,{{text}}</h1><h2 ng-transclude></h2></div>',
};
});
myApp.directive('myInner', function() {
return {
restrict: 'E',
require: '^myOuter',
replace: true,
template: '<span>hi,{{myContent}},{{text}}</span>',
//此处{{text}}失效,是因为父组件有独立scope!!!
link: function(scope, elem, attr, SupControl) {
scope.myContent = SupControl.name;
console.log(SupControl.name);
}
};
});
</script>
</body>
</html>