2013-03-13 67 views
18

我想用anugularJS生成一个n级的层次无序列表,并已能够成功地这样做。但现在,我在指令和控制器之间存在范围问题。我需要在指令模板中通过ng-click调用的函数内更改父级的范围属性。如何正确地绑定指令和控制器之间的角度与angularJS

http://jsfiddle.net/ahonaker/ADukg/2046/ - 这里的JS

var app = angular.module('myApp', []); 

//myApp.directive('myDirective', function() {}); 
//myApp.factory('myService', function() {}); 

function MyCtrl($scope) { 
    $scope.itemselected = "None"; 
    $scope.organizations = { 
     "_id": "SEC Power Generation", 
     "Entity": "OPUNITS", 
     "EntityIDAttribute": "OPUNIT_SEQ_ID", 
     "EntityID": 2, 
     "descendants": ["Eastern Conf Business Unit", "Western Conf Business Unit", "Atlanta", "Sewanee"], 
     children: [{ 
      "_id": "Eastern Conf Business Unit", 
      "Entity": "", 
      "EntityIDAttribute": "", 
      "EntityID": null, 
      "parent": "SEC Power Generation", 
      "descendants": ["Lexington", "Columbia", "Knoxville", "Nashville"], 
      children: [{ 
       "_id": "Lexington", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 10, 
       "parent": "Eastern Conf Business Unit" 
      }, { 
       "_id": "Columbia", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 12, 
       "parent": "Eastern Conf Business Unit" 
      }, { 
       "_id": "Knoxville", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 14, 
       "parent": "Eastern Conf Business Unit" 
      }, { 
       "_id": "Nashville", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 4, 
       "parent": "Eastern Conf Business Unit" 
      }] 
     }] 
    }; 

    $scope.itemSelect = function (ID) { 
     $scope.itemselected = ID; 
    } 
} 

app.directive('navtree', function() { 
    return { 
     template: '<ul><navtree-node ng-repeat="item in items" item="item" itemselected="itemselected"></navtree-node></ul>', 
     restrict: 'E', 
     replace: true, 
     scope: { 
      items: '=' 
     } 
    }; 
}); 

app.directive('navtreeNode', function ($compile) { 
    return { 
     restrict: 'E', 
     template: '<li><a ng-click="itemSelect(item._id)">{{item._id}} - {{itemselected}}</a></li>', 
     scope: { 
      item: "=", 
      itemselected: '=' 
     }, 
     controller: 'MyCtrl', 
     link: function (scope, elm, attrs) { 
      if ((angular.isDefined(scope.item.children)) && (scope.item.children.length > 0)) { 
       var children = $compile('<navtree items="item.children"></navtree>')(scope); 
       elm.append(children); 
      } 
     } 
    }; 
}); 

和这里的HTML

<div ng-controller="MyCtrl"> 
    Selected: {{itemselected}} 

    <navtree items="organizations.children"></navtree> 
</div> 

注意从模型生成的列表。并且ng-click调用该函数来设置父范围属性(itemselected),但这种更改只发生在本地。当我点击一个项目时,预期的行为是“Selected:None”应该改为“Selected:xxx”,其中xxx是被点击的项目。

我没有适当地绑定父范围和指令之间的属性?我如何将属性更改传递给父范围?

希望这是明确的。

在此先感谢您的帮助。

+1

欢迎来到StackOverflow!如果您在接近尾声的时候以实际问题的形式来重述它,它可能会改进您的帖子。 – 2013-03-13 15:20:51

回答

18

请看看这方面的工作小提琴,http://jsfiddle.net/eeuSv/

我做什么,要求navtree-node指令里面的父控制器,并呼吁在控制器中定义的成员函数。 成员函数是setSelected。请注意,其this.setSelected而不是$scope.setSelected。 然后定义一个navtree-node示波器方法itemSelect。单击定位标记时,它将调用navtree-node范围上的itemSelect方法。该内容将调用控制器成员方法setSelected传递选定的ID。

scope.itemSelect = function(id){ myGreatParentControler.setSelected(id) }

+1

谢谢rajkamal ....我让它工作。我仍然对'require'感到困惑:“ngController”'ngController从哪里来? – user2165994 2013-03-13 19:47:18

+3

可以用一般的术语来说:'require:“^ ngController”'语句查找控制器上定义的父元素。由于我们在'navtree'上面定义了控制器'MyCtrl',所以它将作为第四个参数传递给nav-node指令的链接函数。您可以在指令定义对象标题下的http://docs.angularjs.org/guide/directive中阅读此内容。他们在这里定义了选项。 – rajkamal 2013-03-13 20:05:31

+3

+1。尼斯。我还没有看到其他人在使用'require:'^ ngController''的SO上给出了一个例子。我见过的所有其他示例都需要ngModel或其他指令的控制器。 – 2013-03-13 22:26:24

3

这可能是因为每个指令创建了自己的范围(实际上你告诉他们这样做)。
您可以阅读更多关于指令here,特别是“编写指令(长版)”一章。

范围 - 如果设置为:

真正 - 那么一个新的范围将这个指令创建。如果 同一元素上的多个指令请求一个新范围,则仅创建一个新的范围 。新范围规则不适用于模板的根 ,因为模板的根始终获取新的 范围。

{}(object hash) - 然后创建一个新的'isolate'作用域。 “分离”范围与正常范围不同,因为 原型从父范围继承。这在 创建可重用组件(不应该意外读取)或 修改父范围中的数据时非常有用。

因此,您所做的更改未反映在MyCtrl范围内,因为每个指令都有自己的“隔离”范围。

这就是为什么点击只会改变本地$scope.itemselected变量而不是它们的“全部”。

+0

感谢maxdec,但是指令需要有自己的范围才能递归遍历层次结构。我如何获得两全其美的结果:层次结构的本地范围和其他属性的父级范围的访问权限?我读过你链接到ad dause的文档,并认为我理解了,特别是关于“=或= attr - 在本地范围属性和父范围属性之间设置双向绑定”...“&”任何更改到parentModel将反映在localModel中,localModel中的任何更改都将反映在parentModel中。“但我无法完成这项工作。 – user2165994 2013-03-13 18:21:05

+0

@ user2165994,“我该如何获得两全其美:层次结构的本地范围和访问其他属性的父范围?” - 使用'scope:true'。你的指令会得到它自己的作用域[prototypically inherits](http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs/14049482#14049482 )从父范围。所以你可以定义你自己的属性,和/或引用父属性。如果您需要写入父属性,请确保它是对象属性(例如'model.prop1'),而不是基元('prop1')。 – 2013-03-13 22:29:18

11

Maxdec是对的,这与范围界定有关。不幸的是,这是一个足够复杂的情况,AngularJS文档可能会导致初学者(比如我)错误的引导。

警告:在我试图解释这一点时,要让自己有点啰嗦。如果您只想查看代码,请转至此JSFiddle。我还发现egghead.io视频对于了解AngularJS非常有用。

下面是我对这个问题的理解:你有一个指令层次结构(navtree,navitem),并且你希望将信息从navitem“上树”传递到根控制器。 AngularJS就像通常写得很好的Javascript一样,被设置为严格限定变量的范围,这样就不会意外地搞乱页面上运行的其他脚本。

有角中的一个特殊的语法(&),让你俩创造一个隔离范围,并调用父范围功能:

// in your directive 
scope: { 
    parentFunc: '&' 
} 

到目前为止好。事情变得棘手,当你有指令的多层次的,因为你基本上是要做到以下几点:

  1. 有根控制器接受变量的函数和更新模型
  2. 一个中等水平的指令
  3. 可以用根控制器

的问题是沟通的儿童层次的指令,子级指令不能看到根控制器。我的理解是,你必须建立在你的指令结构,它充当遵循“产业链”:

第一:有你的根控制器中的函数,返回一个函数(具有参考根视图控制器范围):

$scope.selectFunctionRoot = function() { 
    return function (ID) { 
     $scope.itemselected = ID; 
    } 
} 

二:设置了中级指令,有它自己的选择功能(它会传递给孩子)返回类似于下面的东西。请注意我们如何保存关闭中级指令的范围,因为当实际执行这段代码,这将是在儿童级指令的情况下:

// in the link function of the mid-level directive. the 'navtreelist' 
scope.selectFunctionMid = function() { 
    // if we don't capture our mid-level scope, then when we call the function in the navtreeNode it won't be able to find the mid-level-scope's functions    
    _scope = scope; 
    return function (item_id) { 
     console.log('mid'); 
     console.log(item_id); 

     // this will be the "root" select function 
     parentSelectFunction = _scope.selectFunction(); 
     parentSelectFunction(item_id); 
    }; 
}; 

三:在子层次指令(navtreeNode)功能绑定到ng-click调用本地功能,这将反过来,“调用链”一路到根控制器:

// in 'navtreeNode' link function 
scope.childSelect = function (item_id) { 
    console.log('child'); 
    console.log(item_id); 

    // this will be the "mid" select function 
    parentSelectFunction = scope.selectFunction(); 
    parentSelectFunction(item_id); 
}; 

下面是更新fork of your JSFiddle,代码中有评论。

+0

不错的anwser!我学到了东西。 – maxdec 2013-03-13 21:48:09

+0

在控制器和指令之间使用范围的双向绑定来共享通用函数的优秀答案。谢谢! – Alex 2014-04-16 08:57:03

相关问题