如何使用angularJS正确绑定指令和控制器之间的范围

use*_*994 18 scope using-directives hierarchy angularjs

我正在尝试使用anugularJS生成一个n级分层无序列表,并且已经能够成功完成.但是现在,我在指令和控制器之间存在范围问题.我需要在指令模板中通过ng-click调用的函数中更改父类的scope属性.

请参阅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);
            }
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

这是HTML

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

    <navtree items="organizations.children"></navtree>
</div>
Run Code Online (Sandbox Code Playgroud)

请注意,列表是从模型生成的.并且ng-click调用该函数来设置父范围属性(itemselected),但更改仅在本地进行.当我单击某个项目时,预期的行为是"Selected:None"应更改为"Selected:xxx",其中xxx是单击的项目.

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

希望这很清楚.

在此先感谢您的帮助.

Raj*_*ian 18

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

我所做的是要求navtree-node指令中的父控制器,并调用该控制器中定义的成员函数.成员函数是setSelected.请注意它的this.setSelected与否$scope.setSelected.然后定义navtree-node范围方法itemSelect.当您单击锚标记时,它将调用范围itemSelect上的方法navtree-node.这个转换将调用控制器成员方法setSelected传递所选的id.

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

  • 一般来说,`require:"^ ngController"`语句查找在其上定义了控制器的父元素.由于我们在`navtree`上面定义了控制器`MyCtrl`,因此它被传递给nav-node指令的链接函数作为第四个参数.您可以在指令定义对象标题下的http://docs.angularjs.org/guide/directive中阅读此内容.他们在这里定义了选项. (3认同)
  • +1.尼斯.我还没有看到其他人在SO上使用`require:'^ ngController'`的例子.我见过的所有其他示例都需要ngModel或其他指令的控制器. (3认同)
  • @rajkamal请将您的答案发布到答案中,而不是仅仅通过JSFiddle.如果JSFiddle失败,你的答案应保持其相关性. (3认同)

Ben*_*nmj 11

Maxdec是对的,这与范围有关.不幸的是,这是一个足够复杂的案例,AngularJS文档可能会误导初学者(像我一样).

警告:当我试图解释这个问题时,让我感到有些啰嗦.如果您只想查看代码,请转到此JSFiddle.我还发现egghead.io视频在学习AngularJS方面非常有价值.

以下是我对该问题的理解:您有一个指令层次结构(navtree,navitem),并且您希望将信息从"在树上"的navitem传递到根控制器.AngularJS,就像编写良好的Javascript一样,设置为严格控制变量的范围,这样你就不会意外搞乱在页面上运行的其他脚本.

&Angular中有一个特殊的语法(),它允许您创建隔离范围并在父范围上调用函数:

// in your directive
scope: {
   parentFunc: '&'
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.当你有多个指令级别时,事情变得棘手,因为你基本上想要做以下事情:

  1. 在根控制器中有一个接受变量并更新模型的函数
  2. 中级指令
  3. 可以与根控制器通信的子级指令

问题是,子级指令无法看到根控制器.我的理解是你必须在你的指令结构中建立一个"链",其作用如下:

第一:在根控制器中有一个函数返回一个函数(它引用了根视图控制器的作用域):

$scope.selectFunctionRoot = function () {
    return function (ID) {
        $scope.itemselected = ID;
    }
}
Run Code Online (Sandbox Code Playgroud)

第二:设置中级指令,使其拥有自己的select函数(它将传递给子元素),返回类似下面的内容.请注意我们如何保存中级指令的范围,因为当实际执行此代码时,它将位于子级指令的上下文中:

// 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);
    };
};
Run Code Online (Sandbox Code Playgroud)

第三:在子级指令(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);
};
Run Code Online (Sandbox Code Playgroud)

这是JSFiddle的更新分支,它在代码中有注释.