如何通过 Angular 中的嵌套指令向下传递包含?

How to pass transclusion down through nested directives in Angular?

我正在尝试弄清楚如何通过嵌套指令向下传递包含并绑定到最内层指令中的数据。将它想象成一个列表类型控件,您可以将它绑定到一个数据列表,而嵌入是您要用来显示数据的模板。这是一个仅绑定到单个值的基本示例(这里是 plunk)。

html

<body ng-app="myApp" ng-controller="AppCtrl as app">
    <outer model="app.data"><div>{{ source.name }}</div></outer>
</body>

javascript

angular.module('myApp', [])

.controller('AppCtrl', [function() {
    var ctrl = this;

    ctrl.data = { name: "Han Solo" };

    ctrl.welcomeMessage = 'Welcome to Angular';
}])

.directive('outer', function(){
    return {
        restrict: 'E',
        transclude: true,
        scope: {
            model: '='
        },
        template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
    };
})

.directive('inner', function(){
    return {
        restrict: 'E',
        transclude: true,
        scope: {
            source: '=myData'
        },
        template :'<div class="inner" my-transclude></div>'
    };
})

.directive('myTransclude', function() {
    return {
        restrict: 'A',
        transclude: 'element',
        link: function(scope, element, attrs, controller, transclude) {
            transclude(scope, function(clone) {
                element.after(clone);
            })
        }
    }
});

如您所见,嵌入的位没有出现。有什么想法吗?

您可以将您的嵌入一直传递到第三个指令,但我看到的问题是范围覆盖。您希望 {{ source.name }} 来自 inner 指令,但是当它在第一个指令中编译时:

template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'

{{ source.name }} 已经使用 outer 的范围编译。我能看到它以你想要的方式工作的唯一方法是用 $compile 手动完成它......但也许比我聪明的人可以想到另一种方法。

Demo Plunker

感谢 Zach 的回答,我找到了解决问题的不同方法。我现在将模板放在一个单独的文件中,并将它的 url 通过作用域向下传递,然后使用 ng-include 将其插入。这是解决方案的 Plunk

html:

<body ng-app="myApp" ng-controller="AppCtrl as app">
    <outer model="app.data" row-template-url="template.html"></outer>
</body>

模板:

<div>{{ source.name }}</div>

javascript:

angular.module('myApp', [])

.controller('AppCtrl', [function() {
    var ctrl = this;

    ctrl.data = { name: "Han Solo" };
}])

.directive('outer', function(){
    return {
        restrict: 'E',
        scope: {
            model: '=',
            rowTemplateUrl: '@'
        },
        template: '<div class="outer"><inner my-data="model" row-template-url="{{ rowTemplateUrl }}"></inner></div>'
    };
})

.directive('inner', function(){
    return {
        restrict: 'E',
        scope: {
            source: '=myData',
            rowTemplateUrl: '@'
        },
        template :'<div class="inner" ng-include="rowTemplateUrl"></div>'
    };
});

在这种情况下,您不必使用自定义嵌入指令或任何技巧。我在您的代码中发现的问题是默认情况下 transclude 被编译到父范围。因此,您可以通过实施指令的 compile 阶段来解决此问题(这发生在 link 阶段之前)。实现类似于下面的代码:

app.directive('inner', function () {
    return {
        restrict: 'E',
        transclude: true,
        scope: {
            source: '=myData'
        },
        template: '<div class="inner" ng-transclude></div>',
        compile: function (tElem, tAttrs, transclude) {
            return function (scope, elem, attrs) { // link

                transclude(scope, function (clone) {
                    elem.children('.inner').append(clone);
                });
            };
        }
    };
});

通过这样做,您将强制您的指令对其隔离范围进行嵌入。