Angularjs 继承嵌套指令中的范围



示例:http://jsfiddle.net/avowkind/PS8UT/

我希望嵌套子指令从其包装父指令(如果存在)获取其数据,否则从外部控制器获取数据。

<div ng-controller="MyCtrl">
    <parent index="1">
        <child></child>
    </parent>
    <parent index="2">
        <child></child>
    </parent>
     <h1>No Parent</h1>
    <child></child>
</div>
<hr>

期望的输出

Parent 1
  Child of parent 1
Parent 2
  Child of parent 2
No Parent
  Child of parent 0

目前我的子对象只看到外部控制器值:

实际输出

Parent 1
  Child of parent 0
Parent 2
  Child of parent 0
No Parent
  Child of parent 0

这是简单的版本;实际上,外部指令从由嵌套子级格式化的服务器获取数据,因此所传达的是复杂对象而不是简单的字符串。此外,子指令是一个可视化,它将在不同的数据集上工作,因此外部父指令并不总是相同的类型。

更一般地说,我试图在这里获得的模式是使用单独的指令来填充模型并查看它。 所以更现实的用法是

<temperature-for city="Auckland">
   <plot/>
   <analysis/>
</temperature-for>
<humidity-for city="Hamilton">
   <plot/>
   <analysis/>
</temperature-for>

<test-data>
   <plot/>
</test-data>

我个人使用的一种不同方法是将绘图和分析指令定义为隔离范围,然后双向绑定所需的输入。

这样,指令就是完全独立的组件,具有显式定义的接口。我亲自做了这样的绘图指令:

<plot data="countries['Auckland'].plot.data" options="countries['Auckland'].plot.options" legend="external" zoom="xy"></plot>
Scope would look like:
scope: {
    data: '=',
    options: '=',
    zoom: '@?',  // '?' indicates optional
    legend: '@?',
}

这样就不会混淆此组件工作所需的数据,并且可以在指令中为所需的输入属性编写文档。

总而言之,这是一种模式,它适用于 AngularJS 中的大部分用例,即只要有可重用性的情况。

编辑:只是想补充一点:查看您的 HTML,绝对没有指示这些指令使用什么,它们可能依赖于任何东西(例如,它们是否从服务中获取所有数据?或者它们是否依赖于父范围?如果是这样,范围是什么?

有几种不同的方法可以做到这一点,但假设你真的想在这里使用父作用域,这是一个配合你的小提琴的解决方案。

var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
  $scope.index = 0;
}
myApp.directive('parent', function () {
  return {
    transclude: true,
    scope: {
      index: '='
    },
    restrict: 'EA',
    template: '<h2>Parent {{ index }}</h2>',
    compile: function(tE, tA, transcludeFn) {
      return function (scope, elem, attrs) {
        elem.append(transcludeFn(scope)[1]);
      };
    }
  }
});
myApp.directive('child', function () {
  return {
    restrict: 'EA',
    scope: false,
    template: '<p>Child of parent {{ index }}</p>'
  }
});

你可以在这里看到你的小提琴的叉子。

这个想法是,通过摆脱ngTranscludeDirective并手动创建嵌入,您可以将嵌入与您选择的范围链接起来。然后,您可以将结果附加到指令编译产生的元素中所需的任何位置。

另一个要点是确保子指令不会创建作用域(根本不创建隔离作用域、已包含作用域还是新作用域)。

我认为这会给你你想要的结果。

注意:请仔细研究您的示波器,因为调整这些行为可能会产生意想不到的结果。

例如,如果将链接函数添加到子指令,并且它将索引设置为 5:

link: function(scope) {
  scope.index = 5;
}

这不会影响嵌套在父项中的子项的scope.items。但是,它会影响外部父范围(在本例中为 MyCtrl 的范围)。任何不在父指令内的指令都会不断更改 MyCtrl 索引。

但是,如果在子链接函数中将新属性添加到范围:

link: function(scope) {
  scope.somethingElse = foo;
}

无论是否嵌套在父指令中,scope.somethingElse都将在父作用域中可用。

您需要

根据父指令范围对其进行排除和细粒度处理,不能使用 ng-transclude 指令:http://jsfiddle.net/PS8UT/2/

var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
    $scope.index = 0;
}
myApp.directive('parent', function ($compile) {
    return {
        scope: {
            index: '@'
        },
        restrict: 'EA',
        transclude: true,
        template: '<h2>Parent {{ index }}</h2>',
        link: function (scope, elem, attrs, ctrl, transclude) {
            transclude(scope, function(clone, s){
                elem.append($compile(clone)(s)); // basically you need to reassign the inner child scope to your current isolated scope
            });
        }
    }
});
myApp.directive('child', function () {
    return {
        //scope: true, // no need to create another scope since you want to use the parent
        // scope: { }, // no index printed
        restrict: 'EA',
        template: '<p>Child of parent {{ index }}</p>'
    }
});

通常,当您处理模板和隐含时,需要父母的遗产是一种痛苦。 ng-transclude不会使用直接父作用域作为父作用域,它通常使用控制器作用域。在角度文档$compile文档中有说明:

隐含 编译元素的内容并使其可用于 命令。 通常与ngTransclude一起使用。优点 嵌入是链接函数接收嵌入 预绑定到正确范围的函数。在典型设置中 该小组件创建一个隔离范围,但嵌入不是 子级,但隔离范围的同级。这使得 具有私有状态的小部件和要绑定到的嵌入 父(预隔离)范围

最新更新