向现有集合添加或修改对象时,ng-repeat within 指令不会更新

ng-repeat within directive will not update when adding or modifying objects to the existing collection

我有一个树视图,其中包含我试图填充的带有嵌套数组的对象。

A working plunker may be found here.

这里是代码结构的细分以及我到目前为止所做的尝试。

在我的 treeview.html 视图中,我正在加载我创建的 treeview 指令: 我目前有两个只是为了测试绑定。在选中事件复选框之前,不会填充第二个。

<H3>Tree View Sameple</H3>

<div>
  <h3>Treeview 1</h3>
    <tree src="tree.treeData" iobj="object" filter="tree.getAggregateInfo(object, isSelected)"></tree>
    <h3>Treeview 2 once object has been updated</h3>
    <tree src="tree.treeData2" iobj="object" filter="tree.getAggregateInfo(object, isSelected)"></tree>
</div>
<hr/>
<p>javascript object data</p>
{{tree.treeData}}

底部绑定只是显示js文件的输出。当您单击事件复选框时在树视图中,我正在模拟一个正在检索的新嵌套集合,该集合应作为子元素插入到树的事件节点下。

这是treeviewController.js

中使用的代码
(function () {

    angular.module('app')
           .controller('treeviewController', ['$log', '$scope', '$timeout', treeviewController]);
    function treeviewController($log, $scope, $timeout) {

        var vm = this;
       //storage container for last node selected in tree
        vm.lastNodeSelected = '';

        vm.treeData2 = {};//test container to reflect object once updated
        //original data not an arry but an object holding an array
        vm.treeData = 
            {
                "children": [
                  {
                      "name": "Document Type",
                      "children": [
                        {
                            "name": "Incident",
                            "documentType": "Document Type",
                            "aggregateUponField": "_ocommon.documenttype",
                            "parent": "",
                            "count": 2950,
                            "children": null
                        },
                        {
                            "name": "Some Event",
                            "documentType": "Document Type",
                            "aggregateUponField": "_ocommon.documenttype",
                            "parent": "",
                            "count": 2736,
                            "children": [{
                                        "name": "Some Event Date",
                                        "children": [
                                                    {
                                                    "name": "2008",
                                                    "documentType": "Incident Date",
                                                    "aggregateUponField": "dateincidentstart",
                                                    "parent": "",
                                                    "count": 451,
                                                    "children": null
                                                    },
                                                    {
                                                    "name": "2009",
                                                    "documentType": "Incident Date",
                                                    "aggregateUponField": "dateincidentstart",
                                                    "parent": "",
                                                    "count": 407,
                                                   "children": null
                                                   },
                                                   {
                                                   "name": "2010",
                                                   "documentType": "Incident Date",
                                                   "aggregateUponField": "dateincidentstart",
                                                   "parent": "",
                                                   "count": 426,
                                                   "children": null
                                                   }
                                                  ]
                                       }]
                        }
                      ]
                  }
                ]
            };

        //aggregate data to be nested under incident
        vm.newChildData = [
              {
                  "name": "Incident Date",
                  "children": [
                    {
                        "name": "2008",
                        "documentType": "Incident Date",
                        "aggregateUponField": "dateincidentstart",
                        "parent": "",
                        "count": 451,
                        "children": null
                    },
                    {
                        "name": "2009",
                        "documentType": "Incident Date",
                        "aggregateUponField": "dateincidentstart",
                        "parent": "",
                        "count": 407,
                        "children": null
                    },
                    {
                        "name": "2010",
                        "documentType": "Incident Date",
                        "aggregateUponField": "dateincidentstart",
                        "parent": "",
                        "count": 426,
                        "children": null
                    }
                  ]
              } 

        ];


        var loadData = function () {
            //stip out array
            var children = vm.treeData.children;
            //search array for matching parent
            $log.info(vm.lastNodeSelected);
            var cnt = children.length;
            for (var i = 0; i < cnt; i++) {
                if (children[i].children) {
                    var innerCnt = children[i].children.length;
                    for (var c = 0; c < innerCnt; c++) {
                        if (children[i].children[c].name === vm.lastNodeSelected) {
                            children[i].children[c].children = vm.newChildData;

                        }
                    }
                }
            }

            //wrap back in object and assign to ngModel
            vm.updatedList = { children };
            // $log.info(updatedList);      

            vm.treeData2 = vm.updatedList;
            $timeout(function() {
                $scope.$apply(function() {
                    vm.treeData = vm.updatedList;
                })} , 0);
        }

        vm.getAggregateInfo = function (node) {

            vm.lastNodeSelected = node.name;
            loadData();
           // $log.info(vm.lastNodeSelected);
        };


    }

})();

以下是 treeview 指令。

angular.module('app')
    .directive('tree', function () {
        return {
            restrict: 'E',
            replace: true,
            scope: {
                t: '=src',
                filter: '&'
            },
            template: '<ul><branch ng-repeat="c in t.children track by $index"  src="c" filter="filter({ object: object, isSelected: isSelected })"></branch></ul>'

        };
    });

angular.module('app')
.directive('branch', function ($compile) {

    return {
        restrict: 'E',
        replace: true,
        scope: {
            b: '=src',
            filter: '&',
            checked: '=ngModel'
        },

        template: '<li class="dir-tree"><input type="checkbox" ng-click="innerCall()" ng-model="b.$$hashKey" ng-change="stateChanged(b.$$hashKey)"  />{{ b.name }}  <span ng-hide="visible"> ({{ b.count }})</span></li>',
        link: function (scope, element, attrs) {

            var clicked = '';
            var hasChildren = angular.isArray(scope.b.children);
            scope.visible = hasChildren;
            if (hasChildren) {
                element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
                $compile(element.contents())(scope);
            }
            element.on('click', function (event) {
                event.stopPropagation();
                if (hasChildren) {
                    element.toggleClass('collapsed');
                }
            });
            scope.stateChanged = function (b) {
                clicked = b;
            };

            scope.innerCall = function () {
                scope.filter({ object: scope.b, isSelected: clicked });
            };
        }
    };
});

我看到的是,当我单击事件复选框时,javascript 对象会更新。您可以在输出中看到这一点,也可以在选中复选框后看到绑定的第二个树视图。

我怀疑这个问题与事件的 $digest 周期有关,并且没有通知树实际更新。我已经尝试过,正如您在控制器中看到的那样,使用 $scope.$apply() 包裹在超时中,但这无法更新原始树。

对数组更新执行 .push() 之类的附加操作,但是尝试将新对象插入现有数组似乎不起作用。

关于如何让树更新和反映子对象有什么建议吗?

此处稍作修改 Plunker 以突出显示(并部分修复)问题。

我在 branch 指令中的 link 函数中添加了以下行:

scope.$watch('b', function (newvalue, oldvalue) {
    if (!oldvalue.children && newvalue.children) {
        scope.visible = true;
        element.append('<tree src="b" filter="filter({ object: object, isSelected: isSelected })"></tree>');
        $compile(element.contents())(scope);
     }
}, true);

当树结构更新时,link函数不会再次调用。因此,如果要添加新的子项,则不会为 Angular.

编译这些子项