徹底弄懂AngularJS中的transclusion

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){})  
轉自
前端亂燉

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章