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是继承了指令的父做用域。

相关文章
相关标签/搜索