Serenader

Learning by sharing

Angular's transclude

误点:directive 里面的 transcludedirective scope 的一个子 scope

angular.module('app', []).controller('ctrl', function ($scope) {
    $scope.data = 'controller data';
    $scope.hello = 'controller hello world';
}).directive('test', function () {
    return {
        restrict: 'E',
        template: '<div>{{data}}<div ng-transclude></div></div>',
        transclude: true,
        link: function (scope) {
            scope.data = 'directive data';
            scope.hello = 'directive hello';
        },
        scope: {
            data: '='
        }
    };
});
<body ng-controller="ctrl">
    <test data="data">
        <span>{{data}}</span>
        <span>{{hello}}</span>
    </test>
</body>
transclude 内的 scope 为 directive 的子 scope 的话,上图就应该都是 directive 的内容,而不会出现 controller hello world 这个数据。
实际上,transclude 内部的 scope 为 controller 的一个非独立子 scope,而非 directive 的。因此,在 transclude 里面所拿到的数据均为 controller 的数据,而非 directive 的。
所以例子中,{{hello}} 则为 controller 的数据。而 {{data}} 的情况又有所不同了。
在这个例子中,directive 作为一个独立的 scope 而存在,而且将 data 作为双向数据绑定与 controller 做了一个绑定,因此,实际上在执行 directive 的代码之后,controller 里面的 $scope.data 就与 directive 里面的 scope.data 同步了,因此为 directive data

directive 里面 link 方法的第五个参数:transclude

directive 里面的 transclude 默认执行情况如上所示,它会从 controller 里面产生一个新的非独立子 scope 。除此之外,也可以利用 directive 的 link 方法的 transclude 来操纵 transclude 的表现。
link: function (scope, element, attr, ctrl, transcludeFn) {

}
link 方法有固定的5个参数,如上所示。其中,transcludeFn 接受一个函数,以及可选的 scope 作为第一个参数:
transcludeFn(scope.$parent, function (clone, scope) {

});

// or
transcludeFn(function (clone, scope) {

});
其中,第一个 scope 参数即为传递给 transclude HTML 的 scope。第二个回调函数即表示对 transclude 的操作。它有两个参数,第一个是 transclude 的 HTML 的拷贝,另外一个则是作用于该 HTML 的 scope。如果有显式地传递 scope 给 transcludeFn ,则该参数为传递进来的 scope ,否则为默认的从 controller 创建的一个新的 子 scope 。
传递 scope.$parenttranscludeFn 这种做法与默认情况下 transclude 的做法最大的区别就是,手动传递进来的 scope 为 controller 的 scope 本身,而默认的 scope 是创建一个 controller 的一个 子 scope 。