您的位置:首页 > Web前端 > AngularJS

angularjs中的transclude参数的理解($tranclude服务以及compile中transclude)

2016-05-04 15:34 621 查看
首先我们来和replace进行一个对比:

<!doctype html>
<html ng-app="MyModule">
<head>
<meta charset="utf-8">
</head>
<body>
<hello>
<div>这里是指令内部的内容。</div>
</hello>
</body>
<script src="framework/angular-1.3.0.14/angular.js"></script>
<script src="replace.js"></script>
</html>
我们看看replace中的代码:

var myModule = angular.module("MyModule", []);
myModule.directive("hello", function() {
return {
restrict:"AE",
template:"<div>Hello everyone!</div>",
replace:true
}
});
这时候我们很清楚的看到下面的DOM结构:



也就是说我们的template内容把<hello></hello>标签内部的div元素移除了,这有时候不是我们希望看到的结果,因为这种方式完全无法完成标签的嵌套!于是才引入了我们的transclude属性。

我们使用transclude来完成标签的嵌套工作:

<!doctype html>
<html ng-app="MyModule">
<head>
<meta charset="utf-8">
</head>
<body>
<hello>
<div>这里是指令内部的内容。</div>
</hello>
</body>
<script src="framework/angular-1.3.0.14/angular.js"></script>
<script src="transclude.js"></script>
</html>
我们看看transcude在内部是如何完成的:

var myModule = angular.module("MyModule", []);
myModule.directive("hello", function() {
return {
restrict:"AE",
transclude:true,
template:"<div>Hello everyone!<div ng-transclude></div></div>"
}
});
很显然首先需要把transclude设置为true,然后在template中进行修改,其中<div ng-transclude></div>就是告诉ng把hello标签内部的内容全部放在ng-transclude所在的位置。

DOM结构如下:



这就比较容易理解了,这里的hello标签没有被替换掉,因为没有指定replace:true,至于内部<div ng-transclude></div>还是会原封不动的保存,只是内部原来的DOM结构被这个ng-transclude指定的div包裹起来了!而且内部原来的DOM也被添加了一个内置的class,也就是ng-scope,这一点一定要注意!

我们在最后给出一个综合的例子:

<html ng-app='expanderModule'>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="ExpanderSimple.css"/>
<script src="framework/angular-1.3.0.14/angular.js"></script>
<script src="ExpanderSimple.js"></script>
</head>
<body>
<!--指定一个controller为SomeController-->
<div ng-controller='SomeController'>
<!--自定义指令expander-->
<expander class='expander' expander-title='title'>
{{text}}
</expander>
</div>
</body>
</html>
我们看看expander指令的签名:

var expanderModule=angular.module('expanderModule', []);
//expander指令
expanderModule.directive('expander', function() {
return {
restrict : 'EA',
replace : true,
//直接把expander替换掉了
transclude : true,
//transclude表示可以嵌套标签
scope : {
title : '=expanderTitle'
//这里是双向绑定,使用了'='进行双向绑定!
},
//直接替换掉,ng-show通过参数'showMe'来决定是否显示。ng-transclude指定了就是expander内部添加到ng-transclude中
template : '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
//这里指定link函数
link : function(scope, element, attrs) {
scope.showMe = false;
scope.toggle = function() {
scope.showMe = !scope.showMe;
}
}
}
});
//这里指定controller控制器
expanderModule.controller('SomeController',function($scope) {
//在$scope中指定title和text
$scope.title = '点击展开';
$scope.text = '这里是内部的内容。';
});

至此,我们的transclude就可以理解了。

<在控制器中注入的$transclude服务>

<div ng-controller="parentController">
<button-bar>
<button class="primary" ng-click="onPrimary1Click()">{{primary1Label}}</button>
<button class="primary">Primary2</button>
<button class="secondary">Secondary1</button>
</button-bar>
</div>
我们看看内部是如何注入$transclude服务的:

var testapp = angular.module('testapp', []);
testapp.controller('parentController', ['$scope', '$window', function($scope, $window) {
$scope.onPrimary1Click = function() {
alert('Primary1 clicked');
};
$scope.primary1Label = "Prime1"
}]);
testapp.directive('primary', function() {
return {
restrict: 'C',
link: function(scope, element, attrs) {
element.addClass('btn btn-primary');
}
}
});
testapp.directive('secondary', function() {
return {
restrict: 'C',
link: function(scope, element, attrs) {
element.addClass('btn');
}
}
});
testapp.directive('buttonBar', function() {
return {
restrict: 'EA',
template: '<div class="span4 well clearfix"><div class="primary-block pull-right"></div><div class="secondary-block"></div></div>',
replace: true,
transclude: true,
scope: {},//这里是独立作用域
//我们在这里注入了$scope,$element,$transclude。这个控制器应该是指令的控制器
controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {
$transclude(function(clone) {
//$transclude中接收的函数里的参数含有指令元素的内容(指令元素的内容就是指令内部的元素,也就是应该被transclude的内容,这里指的是[text, button.primary.ng-scope, text, button.primary.ng-scope, text, button.secondary.ng-scope, text])
//$element包含编译后的DOM元素(也就是把指令template进行了编译,这里指的是[div.span4.well.clearfix]),所以就可以在控制器中同时操作DOM元素和指令内容。
var primaryBlock = $element.find('div.primary-block');
var secondaryBlock = $element.find('div.secondary-block');
var transcludedButtons = clone.filter(':button');
angular.forEach(transcludedButtons, function(e) {
//每一个对象都有hasClass方法等
if (angular.element(e).hasClass('primary')) {
primaryBlock.append(e);
} else if (angular.element(e).hasClass('secondary')) {
secondaryBlock.append(e);
}
});
});
}],
};
});
最重要的是在我们的自定义指令中指定了一个controller函数:

controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {
$transclude(function(clone) {
//$transclude中接收的函数里的参数含有指令元素的内容(指令元素的内容就是指令内部的元素,也就是应该被transclude的内容,这里指的是[text, button.primary.ng-scope, text, button.primary.ng-scope, text, button.secondary.ng-scope, text])
//$element包含编译后的DOM元素(也就是把指令template进行了编译,这里指的是[div.span4.well.clearfix]),所以就可以在控制器中同时操作DOM元素和指令内容。
var primaryBlock = $element.find('div.primary-block');
var secondaryBlock = $element.find('div.secondary-block');
var transcludedButtons = clone.filter(':button');
angular.forEach(transcludedButtons, function(e) {
//每一个对象都有hasClass方法等
if (angular.element(e).hasClass('primary')) {
primaryBlock.append(e);
} else if (angular.element(e).hasClass('secondary')) {
secondaryBlock.append(e);
}
});
});
}],
};
这时候我们在$transclude中传入的函数接受一个参数,这个参数代表我们需要通过transclude包含的内容,说白了也就是我们的自定义指定内部的子元素集合!而$element就是我们指令的template编译后得到的DOM结构:我们看看这个接受到的clone的签名是怎么样的:






很显然就是我们在页面中自定义指令内部的元素进行编译后的DOM元素,有一点注意:这个编译后的DOM含有空格,也就是text元素类型。但是我们可能只是需要其中的button元素,于是在上面引入了filter方法,但是一直报错说"angular.js:62 TypeError: clone.filter is not a function",但是这个例子确实是可以运行的,所以这里我记录下来,以后慢慢理解。我们看看这时候ng为我们提供了那些方法,这些方法都是来源于jqLite的:





很显然如果我们需要对DOM进行操作,这些方法都是可以调用的。

<编译函数参数中的transclude>:

DOM结构和上面一样,我们看看指令内部的代码:

var testapp = angular.module('testapp', []);
//这里注入了$window对象
testapp.controller('parentController', ['$scope', '$window', function($scope, $window) {
$scope.primary1Label = 'Prime1';
$scope.onPrimary1Click = function() {
$window.alert('Primary 1 clicked');
}
}]);
testapp.directive('primary', function() {
return {
restrict: 'C',
link: function(scope, element, attrs) {
element.addClass('btn btn-primary');
}
}
});
testapp.directive('secondary', function() {
return {
restrict: 'C',
link: function(scope, element, attrs) {
element.addClass('btn');
}
}
});
//这里是我们的指令buttonBar
testapp.directive('buttonBar', function() {
return {
restrict: 'EA',
template: '<div class="span4 well clearfix"><div class="primary-block pull-right"></div><div class="secondary-block"></div></div>',
replace: true,
transclude: true,
//为我们的编译函数参数中的transclude
compile: function(elem, attrs, transcludeFn) {
return function (scope, element, attrs) {
//这里我们指定了第一个参数为scope
transcludeFn(scope, function(clone) {
var primaryBlock = elem.find('div.primary-block');
var secondaryBlock = elem.find('div.secondary-block');
//这里的clone就是我们的应该被transclude的内容的DOM结构,当然其中也是包含空格符号的,所以需要过滤
console.log(clone);
var transcludedButtons = clone.filter(':button');
//对我们的button元素进行遍历
angular.forEach(transcludedButtons, function(e) {
if (angular.element(e).hasClass('primary')) {
primaryBlock.append(e);
} else if (angular.element(e).hasClass('secondary')) {
secondaryBlock.append(e);
}
});
});
};
}
};
});
其实这种方式是在我们自定义指令中使用了一个compile函数,并且在编译函数compile中指定了transclude参数:

compile: function(elem, attrs, transcludeFn) {
return function (scope, element, attrs) {
//这里我们指定了第一个参数为scope
transcludeFn(scope, function(clone) {
var primaryBlock = elem.find('div.primary-block');
var secondaryBlock = elem.find('div.secondary-block');
//这里的clone就是我们的应该被transclude的内容的DOM结构,当然其中也是包含空格符号的,所以需要过滤
var transcludedButtons = clone.filter(':button');
//对我们的button元素进行遍历
angular.forEach(transcludedButtons, function(e) {
if (angular.element(e).hasClass('primary')) {
primaryBlock.append(e);
} else if (angular.element(e).hasClass('secondary')) {
secondaryBlock.append(e);
}
});
});
};
}
我们为transclude函数传入了第一个参数为scope,同时第二个参数为一个函数,这个函数接受来自于自定义指令的内部元素,也就是需要被添加到ng-transclude指令中的元素。而外层函数compile接受第一个参数为elem,其代表的对template进行编译后的DOM树,第三个参数才是我们的transclude函数。阅读angular中的transclude一文定能收获不少
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: