AngularJS的ngTransclude

2018-06-07 18:29 更新

transclued的定義

Transclude好像并不是一個英語單詞,有道詞典沒有,百度翻譯的意思是嵌入的意思。transclude在angularjs的自定義directive中是比較常見的一個東西,所以非常有必要了解一下。

我們首先看下官方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}}"</pane>
</div>

這里的pane是一個自定義directive,標簽里還有一個表達式,這個指令的目的是顯示input中輸入title,和textarea中輸入的text,當然是按照一定的dom結(jié)構(gòu)顯示。看下pane是如何實現(xiàn):


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="AngularJS的ngTransclude"></pane>AngularJS的ngTransclude變量的內(nèi)容封裝到我們的dom結(jié)構(gòu)中,AngularJS的ngTransclude可以通過結(jié)構(gòu)scope: { title:'@' }取得,但是我們想要保留<pane></pane>標簽里的東西(有可能會是很多的表達式和dom結(jié)構(gòu)),那就需要今天的主角transclude了。

這個例子的結(jié)果生成的dom結(jié)構(gòu)是這樣的:


<div style="border: 1px solid black;">
    <div style="background-color: gray">我是標題</div>
    我是內(nèi)容
</div>

有個這樣結(jié)果我想你就看明白了。原來模板中的<div ng-transclude></div>最后會被<pane></pane>標簽里的表達式內(nèi)容所替換。這是就是transclude的用途。

我們回過頭再來看ng-transclude的定義:

  • ng-transclude指明的是一個插入的位置
  • 指令中標簽里的元素都會先刪除然后被嵌入包含后的內(nèi)容所替換

這個例子夠簡單,這也是最基礎(chǔ)的用法,我們再來看下高級一點的用法。我們對上面的例子進行擴充,加上了類型和時間:


<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>

最終的目的是這樣的:


<div style="border: 1px solid black;">
    <div style="background-color: gray">
        我是標題<span class="time">我是時間</span>
    </div>
    <p class="type">我是分類</p>
    <p class="content">我是內(nèi)容</p>
</div>

光一個ng-transclude是不行的,當然你也可以像title那樣傳參,但現(xiàn)在是在學習transclude,沒有transclude還學個毛啊。我們有兩種方法可以實現(xiàn)這個目的。

使用compile函數(shù)的transclude參數(shù)

先看pane的directive代碼:


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){})作用域和嵌入包含的內(nèi)容,clone嵌入的內(nèi)容是被jquery封裝過的,有了它,我們就可以做任何想要做的dom操作了。

在controller里注入$transclude

先上代碼:


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這兩個函數(shù)執(zhí)行的地方不同。里面是一模一樣的。

還有一個需要說明的是transclude的作用域的問題。

在官方文檔中提到過deretive的作用域是單獨的,transclude也創(chuàng)建了一個單獨的作用域,而且與derectvie的作用域是平行的,還是拿上面的例子來說。


<div ng-controller="Ctrl">...</div>

首先controller Ctrl會創(chuàng)建一個作用域scope1,derective Pane會在scope1下面創(chuàng)建一個scope2,scope1 包含 scope2,tranclude又會在scope1下面創(chuàng)建一個scope3,scope1也包含scope3,scope2和scope3是兄弟關(guān)系,平行的兩個子作用域。

好,我們可以來驗證一下:

在controller中加日志,


function Ctrl($scope) {
    $scope.title = 'Lorem Ipsum';
    $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
    console.log('scope1', $scope);
}

在derective中添加日志,


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) {
                console.log('scope2', $scope)
                $transclude(function(clone, scope) {
                    console.log('scope3', 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)
                });
            }
        ],
    };
});

在控制臺可以看到,


scope1 a {$id: "003", this: a, $listeners: Object, $parent: e, $childTail: null…}
scope2 e {$id: "004", $childTail: null, $childHead: null, $prevSibling: null, $nextSibling: null…}
scope3 a {$id: "005", this: a, $listeners: Object, $parent: a, $childTail: null…}

點開scope2scope3,就能看到$parent的Id為003,這就印證了我們的觀點。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號