AngularJS中指令的重要性是不言而喻的,指令让我们可以创建自己的HTML标记,它将自定义元素变成了一个一个的模块,极大的体现了前端开发中的模块化模式,并提高了代码的易读性和重用性。AngularJS中的指令也是学习AngularJS中的一个难点所在,其中的许多属性,需要反复学习,认真体会,方能领悟其中的精妙之处。

今天我们要讲的就是其中一个重点和难点 – transclusion。关于这个话题我之前也写过很多文章来讲述,但是当时都是照搬博文中的例子,自己也没有比较深刻的体会,因此一直不得要领。今天我们的目标就是“彻底弄懂transclusion”。

1.什么是transclusion

我们要创建一个指令,我们把这个指令叫做<handsome-me>。在此先略过这个指令的创建过程,如果你还不知道怎样创建一个指令,请前参看前面几篇文章。好了,无论怎么说,这个指令已经创建好了,于是我们可以有以下几种用法:

第一种:<handsome-me/>  

就像是input一样我们叫它“自开自闭”标签。
第二种:<handsome-me></handsome-me>  
就像是div一样对吧,更简单,我们叫它“自开别人闭”(同上同上)标签。

第三种:

<handsome-me>
            //中间有好多代码
</handsome-me>  
和第二种又有点区别,我们叫它“自开别人闭,中间加一坨”(总是感觉好粗俗。。。never mind。。。)标签。

我们来对比以下三种标签,它们有什么区别。当然区别很多。但是具体到我们今天的话题,与之相关的最大的区别就是前两种中间没有加一坨,第三种中间加了一坨。OK,因此我们现在来总结什么叫做transclusion

如果你在定义指令的时候,想要它在具体使用时中间加一坨,那么你就要用transclusion

知道了定义以后,我们要开始来看看具体怎么使用transclusion了。如果你了解AngularJS指令的编写,你一定知道return的那个对象的tranclude指令默认是false,因此如果你想要开启使用transclusion的话,就要将这个transclude属性赋上一个别的值,当然,这个值不能乱赋,它只有两种选择:

第一种选择:transclude: true  
第二种选择:transclude: 'element'  


最常用的呢,是第一种,也就是赋值为true。还记得transclusion的中文意思吗,“嵌入”对吧!因此我们现在就不说“一坨”,而把中间的这一坨叫做“嵌入部分”。ok,回到正题,当transclude是true的时候,嵌入部分就是嵌入部分,比如说:

<handsome-me>
    `name`
<handsome-me>

在transclude:true的时候,它的嵌入部分是什么啊?对了,就是`name`。

<handsome-me>
    <div>
        <span>`name`</span>
    </div>
<handsome-me>

在transclude:true的时候,它的嵌入部分是什么啊?对了,是

<div>
        <span>`name`</span>
</div>

 

当transclude的值是element的时候,又是怎样一种情形。此时,嵌入部分变成了原来的嵌入部分加上外边的自定义标签,也就是整个元素

<handsome-me>
    `name`
<handsome-me>

在transclude:’element’的时候它的嵌入部分是什么啊?对了,是:

<handsome-me>
    <div>
        <span>`name`</span>
    </div>
<handsome-me>

 

2.ng-transclude的作用是什么

 在编写指令时,我们都会有一个template或者templateUrl这样的属性是吧。在使用transclusion时,我们要把嵌入部分放到模板中,因此我们有两种选择,其中一种选择就是使用ng-transclude。ng-tranclude决定了在什么地方放置嵌入部分。

 

假设指令是这样的:

<handsome-me>
    `name`
</handsome-me>

而模板是这样的:

<div>

   <p>MaMa does not need to worry about my study anymore! </p>

   <div ng-transclude></div>

</div>  

于是,在transclude:true的情况下,最终呈现在页面中的HTML会是什么样子。对了,是这样:

<div>
    <p>MaMa does not need to worry about my study anymore! </p>
    `name` 
</div> 

 

另一种情况,在transclude:’element’的情况下,最终呈现在页面中的HTML会是什么样子。对了,是这样:

<div>
    <p>MaMa does not need to worry about my study anymore! </p>
    <handsome-me>
        `name`
    </handsome-me>
</div>

3.不使用ng-transclude的情形

        现在我们来想一个问题,如果我想把我的嵌入部分多次放入我的模板中怎么办?你可能会说,那就多放几个ng-transclude呗!这当然是不行的,在AngularJS中你只在一个指令的模板中只能申明一个ng-tranclude。所以这种情况下我们就能使用模板了,因此我们要使用一个叫做tranclude()的函数!!

        在link函数中,transclude是link函数的第五个参数;在compile函数中,transclude是compile函数的第三个参数。在这个两个函数中,由于我们没有使用依赖注入,因此只要顺序对了就对了,随便命名为什么都可以。而在controller函数中,由于使用的是依赖注入,因此transclude是$transclude,只要名字写对了就对了。在link,compile和controller函数中,transclude的用法一模一样,因此在这我们只举一个link函数的例子:

1.最简单的用法:

link(scope,elem,attrs,ctrl,transclude){
   var content = transclude();
   elem.append(content);
}

在这里,我们通过transclude()返回了嵌入部分的具体内容,然后append到了元素的elem的尾巴上,当然,你想要append多次也是可以的。目前尚未成功append多次

2.复杂一点的用法:

link(scope,elem,attrs,ctrl,transclude){
   tranclude(scope,function(clone){
       elem.append(clone);
   })
}

这里tranclude接受了两个参数,第一个是scope,代表作用域。第二个回调函数中带有一个参数clone,其实它就是嵌入内容,和transclude()的返回值一模一样。那么前面的第一个参数的scope有什么用呢?这就要说到transclude和作用域了!

4.transclude和scope

在定义一个指令时,如果不显式声明scope,那么指令的作用域就是父作用域。如果声明scope:true或者scope:{},那么指令会生成一个自己的作用域,只不过一个原型继承,一个独立而已。如果你使用transclusion,那么无论什么情况,都会生成一个新的作用域,这个作用域直接原型继承于父作用域,它的地位和指令生成的作用域是一样的,二者属于并列的关系。

于是我们现在就能了解tranclude(scope,function(clone){})中的scope是什么意思了,默认情况下,如果我们简单使用translude(),那么作用域默认的是transclude生成的自作用域。但是如果我们使用tranclude(scope,function(clone){}),那么作用域显然就是directive的作用域了。要是我们想使用父作用域怎么办,很简单:

tranclude(scope.$parent,function(clone){})

要是想要一个新的作用域怎么办,也很简单:

tranclude(scope.$parent.$new(),function(clone){})  
转自
前端乱炖