transclude好像不是一个英语单词,有道词典里没有,百度翻译的意思是嵌入。
transclude在angularjs的自定义的derective中是比较常见的一个东西,所有有必要要了解它。
我们首先看下官方api对ng-transclude的解释:
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
官方例子:
<div ng-controller="Ctrl">
<input ng-model="title"><br>
<textarea ng-model="text"></textarea> <br/>
<pane title="{{title}}">{{text}}</pane>
</div>
pane是一个自定义derective,标签里还有一个表达式,这个指令的目的是显示 input中输入的title,和textarea中输入的text,当然是按照一定的dom结构显示。看下pane是如何实现:
<!-- lang: js -->
app.directive('pane', function() {
return {
restrict: 'E',
transclude: true,
scope: { title:'@' },
template:
'<div style="border: 1px solid black;">' +
'<div style="background-color: gray">{{title}}</div>' +
'<div ng-transclude></div>' +
'</div>'
};
});
首先是我们想把 <pane title="{{title}}">{{text}}</pane>
中{{title}}
和{{text}}
变量的内容封装到我们的dom结构中,{{title}}
可以通过结构scope: { title:'@' }
取得,但是我们想要保留 <pane></pane>
标签里的东西(有可能会是很多的表达式和dom结构),那就需要今天的主角 transclude
了。
这个例子的结果生成的dom结构是这样的:
<div style="border: 1px solid black;">
<div style="background-color: gray">我是标题</div>
我是内容
</div>
有个这样结果我想你就看明白了。原来模板中的 <div ng-transclude></div>
最后会被<pane></pane>
标签里的表达式内容所替换。这是就是transclude
的用途。
我们回过头再来看ng-transclude的定义:
这个例子够简单,这也是最基础的用法,我们再来看下高级一点的用法。我们对上面的例子进行扩充,加上了类型和时间:
<div ng-controller="Ctrl">
<input ng-model="title"><br>
<input ng-model="type"><br>
<input ng-model="time"><br>
<textarea ng-model="text"></textarea> <br/>
<pane title="{{title}}">
<span class="time">time</p>
<p class="type">{{type}}<p>
<p class="content">{{text}}<p>
</pane>
</div>
最终的目的是这样的:
<!-- lang: js -->
<div style="border: 1px solid black;">
<div style="background-color: gray">
我是标题<span class="time">我是时间</span>
</div>
<p class="type">我是分类</p>
<p class="content">我是内容</p>
</div>
光一个ng-transclude是不行的,当然你也可以像title那样传参,但现在是在学习transclude,没有transclude还学个毛啊。我们有两种方法可以实现这个目的。
先看pane derective的代码:
<!-- lang: js -->
app.directive('pane', function() {
return {
restrict: 'EA',
template:
'<div style="border: 1px solid black;">' +
'<div class="title" style="background-olor: gray">{{title}}</div>' +
'</div>',
replace: true,
transclude: true,
compile: function(element, attrs, transcludeFn) {
return function (scope, element, attrs) {
transcludeFn(scope, function(clone) {
var title= element.find('title');
var time = clone.find('.time');
var type = clone.find('.type');
var text= clone.find('.content');
title.append(time);
element.append(type);
element.append(text)
});
};
}
};
});
transcludeFn是一个function:transcludeFn(scope, function(clone){})
作用域和嵌入包含的内容,clone嵌入的内容是被jquery封装过的,有了它,我们就可以做任何想要做的dom操作了。
还是先上代码:
<!-- lang: js -->
app.directive('pane', function() {
return {
restrict: 'EA',
template:
'<div style="border: 1px solid black;">' +
'<div class="title" style="background-olor: gray">{{title}}</div>' +
'</div>',
replace: true,
transclude: true,
controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {
$transclude(function(clone, scope) {
var title= element.find('title');
var time = clone.find('.time');
var type = clone.find('.type');
var text= clone.find('.content');
title.append(time);
element.append(type);
element.append(text)
});
}],
};
});
换汤不换药,其实就是 $transclude,transcludeFn这两个函数执行的地方不同。里面是一模一样的。
Ok,transclude大概就这些了,好有一个需要说明的是transclude的作用域的问题。
在官方文档中提到过deretive的作用域是单独的,transclude也创建了一个单独的作用域,而且与derectvie的作用域是平行的,还是拿上面的例子来说。