angular高級篇之transclude使用詳解

angular指令的transclude屬性是一個讓初學者比較難以理解的地方,transclude能夠設置爲false(默認),true或者對象三種值,若是不設該屬性就默認爲false,也就是說你不須要將該指令所在元素包含的內容嵌入到模板中。html

當transclude爲true的時候,這時指令所在元素包含的內容會被嵌入到模板中有ng-transclude指令的元素中,例如:node

index.htmlapp

<!DOCTYPE html>
<html ng-app="myapp">
<head>
    <meta charset="utf-8">
    <title>angular test</title>
</head>
<body ng-controller="myCtrl">
    <div hello="{{name}}">你好</div>
</body>
<script src="./node_modules/angular/angular.js"></script>
<script src="./index.js"></script>
</html>

index.js函數

let app = angular.module('myapp',[]);
app.controller('myCtrl', $scope =>{
    $scope.name = "Jhon";
});
app.directive('hello', () =>{
    return {
        restrict: 'A',
        template: '<div><span ng-transclude></span>{{name}}</div>',
        transclude: true,
        scope:{
            name: "@hello"
        }
    }
});

運行以後的效果以下:spa

<div hello="Jhon" class="ng-isolate-scope">
    <div class="ng-binding">
        <span ng-transclude="">你好</span>
        Jhon
    </div>
</div>

當指令元素包含的內容須要嵌入到指令模板不一樣地方的時候,這個時候就要把transclude設置爲對象,例以下面這個我在項目中使用的一個例子:rest

index.htmlcode

<!DOCTYPE html>
<html ng-app="myapp">
<head>
    <meta charset="utf-8">
    <title>angular test</title>
</head>
<body ng-controller="myCtrl">
    <panel>
        <panel-header>{{title}}</panel-header>
        <panel-body>{{content}}</panel-body>
        <panel-footer>{{footer}}</panel-footer>
    </panel>
</body>
<script src="./node_modules/angular/angular.js"></script>
<script src="./index.js"></script>
</html>

index.jshtm

let app = angular.module('myapp',[]);
app.controller('myCtrl', ['$scope', $scope =>{
    $scope.title = "標題";
    $scope.content = "內容";
    $scope.footer = "頁腳";
}]);
app.directive('panel', () =>{
    return {
        restrict: 'E',
        replace: true,
        transclude: {
            'header': '?panelHeader',
            'body': 'panelBody',
            'footer': '?panelFooter'
        },
        template: `
            <div class="panel">
                <div class="panel-header" ng-transclude="header"></div>
                <div class="panel-body" ng-transclude="body"></div>
                <div class="panel-footer" ng-transclude="footer"></div>
            </div>`
    }
});

顯示結果以下:對象

<div class="panel">
    <div class="panel-header" ng-transclude="header">
        <panel-header class="ng-binding ng-scope">
            標題
        </panel-header>
    </div>
    <div class="panel-body" ng-transclude="body">
        <panel-body class="ng-binding ng-scope">
            內容
        </panel-body>
    </div>
    <div class="panel-footer" ng-transclude="footer">
        <panel-footer class="ng-binding ng-scope">
            頁腳
        </panel-footer>
    </div>
</div>

這裏指令元素內部有三個指令,這三個指令必須以E的形式調用,它們分別要插入到模板的不一樣位置,tranclude指定了要插入的位置,transclude是一個鍵值對的對象,key指定了要插入模板的位置,value就是要插入的內容,?表明這個嵌入點不必定有指令存在,不然必須在這個點插入指令,否則會報錯。blog

值得注意的是,這個實例也證實了一點,指令包含的元素的做用域繼承自指令的父做用域而不是隔離做用域。

 

除了使用ng-transclude指令指定內容嵌入的地方外,咱們還有兩種方法能夠作到這點。

第一種就是在控制器中使用$transclude服務,例如如下代碼:

index.html

<!DOCTYPE html>
<html ng-app="myapp">
<head>
    <meta charset="utf-8">
    <title>angular test</title>
</head>
<body ng-controller="myCtrl">
    <hello name="{{name}}"><span>{{action}}</span></hello>
</body>
<script src="./node_modules/angular/angular.js"></script>
<script src="./index.js"></script>
</html>

index.js

let app = angular.module('myapp',[]);
app.controller('myCtrl', ['$scope', $scope =>{
    $scope.name = "Jhon";
    $scope.action = "你好";
}]);
app.directive('hello', () =>{
    return {
        restrict: 'E',
        transclude: true,
        controller: ['$scope', '$element', '$transclude', ($scope, $element, $transclude) =>{
            $transclude(clone =>{
                //$element.find只能經過標籤名進行查找
                $element.find('span').append(clone);
            });
        }],
        template: '<div><span></span>{{name}}</div>',
        scope: {
            name: '@'
        }
    }
});

最後顯示的結果以下:

<hello name="Jhon" class="ng-isolate-scope">
    <div class="ng-binding">
        <span>
            <span class="ng-binding ng-scope">
                你好
            </span>
        </span>
        Jhon
    </div>
</hello>

其中控制器中的$element就是編譯了以後的模板,而$transclude中的參數clone則是被編譯了的指令包含的內容。二者能夠同時對模板和內容進行處理。

 

另外一種方法是在compile中指定第三個transcludeFn參數,以下所示:

 index.js

let app = angular.module('myapp',[]);
app.controller('myCtrl', ['$scope', $scope =>{
    $scope.name = "Jhon";
    $scope.action = "你好";
}]);
app.directive('hello', () =>{
    return {
        restrict: 'E',
        transclude: true,
        compile: (tElement, tAttrs, transcludeFn) =>{
            return (scope, element, attrs) =>{
                scope.action = "hello";
                transcludeFn(scope, (clone) =>{
                    element.find('span').append(clone);
                });
            };
        },
        template: '<div><span></span>{{name}}</div>',
        scope: {
            name: '@'
        }
    }
});

這個文件實現了以上控制器中相同的功能,transcludeFn接受兩個參數,一個scope做用域,一個函數,和controller同樣,這個函數的參數clone就是編譯以後的要嵌入的內容。惟一不一樣的是,編譯這個clone的做用域是傳進去的第一個參數,而controller中clone是繼承了指令的父做用域。

相關文章
相關標籤/搜索