angularjs在嵌套指令中继承范围

时间:2014-04-01 19:40:39

标签: 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>

3 个答案:

答案 0 :(得分:3)

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

这种方式是完全独立的组件,带有显式的,定义的接口。我亲自制定了这样的策划指令:

<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,绝对没有迹象表明这些指令使用的是什么,它们可能依赖于任何东西(例如,它们是从服务获得所有数据吗?还是它们依赖于父范围?如果是,什么范围?)

答案 1 :(得分:2)

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

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>'
  }
});

你可以看到你的小提琴here

我们的想法是,通过删除ngTranscludeDirective并手动创建转换,您可以将转换与您选择的范围相关联。然后,您可以将结果附加到您指定编译的元素中的任何位置。

另一个要点是确保子指令不创建范围(根本不包括隔离范围,转换范围或新范围)。

我认为这会为您提供您所要求的结果。

注意:很好地研究您的范围,因为调整这些行为可能会产生意外结果。

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

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

对于嵌套在父级中的子级,这不会影响scope.items。但是它会影响外部父作用域(在本例中为MyCtrl的作用域)。任何不在父指令内的指令都会改变MyCtrl索引。

但是,如果在子链接函数中向作用域添加新属性:

link: function(scope) {
  scope.somethingElse = foo;
}
无论是否嵌套在父指令中,

scope.somethingElse都可用于父作用域。

答案 2 :(得分:0)

您需要根据父指令范围对其进行转换和细化,不能使用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 docs中声明:

  

transclude   编译元素的内容并使其可用于   指示。通常与ngTransclude一起使用。的优点   转换是链接功能接收转换   预先绑定到正确范围的函数。在典型的设置中   窗口小部件创建了一个隔离范围,但是转换不是一个   孩子,但孤立范围的兄弟。这使得它成为可能   具有私有状态的小部件,以及要绑定的转换   父(隔离前)范围