【问题标题】:Update an angular treeview directive when a new object is added to the collection将新对象添加到集合时更新角度树视图指令
【发布时间】:2016-10-13 00:33:59
【问题描述】:

我有一个树视图指令归功于http://codepen.io/bachly/pen/KwWrzG,因为它是我的起点。当我将对象添加到集合中时,我正在尝试更新。我可以更新对象并插入新对象,但是一旦更新了 $scoped 项,就永远不会调用 treeview 指令。 最终,使用的数据将来自服务,此时我只是使用模拟数据进行测试。

原来的集合是这样的

$scope.myList = {
            children: [
              {
                  name: "Event",
                  children: [
                    {
                        name: "Event Date",
                        parent:"Event",
                        children: [
                          {
                              name: "2008",
                              filterType: '_eventStartDate',
                              parent: 'Event'
                          },
                          {
                              name: "2009",
                              filterType: '_eventStartDate',
                              parent: 'Event'
                          }
                        ]
                    },
                    {
                        name: "Event Attendee",
                        parent: "Event",
                        children: [
                          {
                              name: "Person 1",
                              filterType: '_eventAttenddeeName',
                              parent: 'Event Attendee'
                          },
                          {
                              name: "Person 2",
                              filterType: '_eventAttenddeeName',
                              parent: 'Event Attendee'
                          }
                        ]
                    }
                  ]
              }]
   };


   var TheOtherCollection = {
       children: [
         {
             name: "A New Event",
             children: [
               {
                   name: "The Other Date",
                   parent: " A New Event",
                   children: [
                     {
                         name: "2010",
                         FilterType: '_eventStartDate',
                         Parent: '_event'
                     },
                     {
                         name: "2011",
                         FilterType: '_eventStartDate',
                         Parent: '_event'
                     }
                   ]
               }
             ]
         }]
   };

这会使用以下指令和 html 生成带有复选框的树视图

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

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

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

            template: '<li><input type="checkbox" ng-click="innerCall()" ng-model="b.$$hashKey" ng-change="stateChanged(b.$$hashKey)" ng-hide="visible" /><a>{{ b.name }}</a></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"></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 });
                };
            }
        };
    });

然后是html

  <div ng-controller="treeController">
        <tree src="myList" iobj="object" filter="doSomething(object, isSelected)"></tree>

        <a ng-click="clicked()"> link</a>
    </div>

当一个复选框被点击时,新的集合使用 lodashjs 添加到现有的集合中

ng-点击事件

$scope.doSomething = function (object, isSelected) {
    if (isSelected) {
        var item = object;
        console.log(item);
        nestAssociation(object, $scope.myList, TheOtherCollection);
    }
}

创建新数组并将其添加到子数组中

function nestAssociation(node, oldCollection, newAggregates) {
    // var item = fn(oldCollection, node.parent);
    var updatedArray = _.concat(oldCollection.children, newAggregates);
    console.log(updatedArray);
    if (updatedArray != null)
        updateMyList(updatedArray);
}

我可以在输出中看到我有一个新对象,但我无法更新树视图。我曾尝试在指令中在指令中的 click 事件上添加 $compile(element) ,但由于尚未构建数组,因此没有任何变化。

我是否需要在该指令中添加 $watch,如果需要,在哪里或有其他方法可以让指令重新渲染并显示新的嵌套集合?

更新

根据这里的一些反馈和问题,可以更详细地了解该问题。我看到的问题不在指令中,因为围绕问题移动数据是,一旦将数组添加到现有模型中,我就无法重新渲染树视图。 以下链接is a working plunker 显示了该项目当前的工作方式。 运行 chrome dev tools 我可以在输出中看到模型在选中复选框后更新

虽然我看到对象已更新,但指令永远不会更新以显示添加到对象的新数组。这是我需要帮助理解的部分。

提前致谢

【问题讨论】:

  • 如果您可以创建一个 plunkr 或 codepen,我们可以看到实际问题并在您的代码中工作,那就太好了(据我了解,链接的 codepen 只是您的起点)。
  • 你能把你的updateMyList函数的代码贴出来,它被nestAssociation()调用。我想这就是你在$scope.myList 中实际保存更新数组的地方
  • 我已经用 plunker 链接更新了我的问题
  • @rlcrews 我添加了更多信息并添加了一个新的 plunker
  • 我认为最新的 plunker 正在工作,请参阅 Clicked 事件。如果它没有按您的意愿工作,请告诉我们。

标签: javascript angularjs angularjs-directive


【解决方案1】:

您将函数传递给内部指令(这是最佳实践),但您可以访问scope.filter。不是doSomethingFunction。这个是undefined 那里。

filter="doSomething(object, isSelected)"
=>
filter="filter(object, isSelected)"

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

下一步:

你永远不能在 angularJS 中访问 $$ 变量,因为它们是私有的。也许您应该从您的数据库中制作一个...,但$$hashkey 似乎是一个简单的解决方案。

checked 属性可能会引发错误,因为您的树指令模板中不存在ngModel。 (在前面至少放一个?

复选框不能具有 $$hashkey 作为模型。
ng-change 和 ng-click 总是会同时调用,用最简单的。

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

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

            template: '<li><input type="checkbox" ng-click="innerCall(b.$$hashKey)" ng-model="isChecked" ng-hide="visible" /><a>{{ b.name }}</a></li>',
            link: function (scope, element, attrs) {

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

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

                };
            }
        };
    });

更新

您的tree 指令和index.html 视图中具有相同的treeController这就是导致视图不更新的原因!

我删除了您指令中的那个,否则您将为每个孩子都有一个控制器。
您在控制器中看到了良好的 console.log 消息,但它位于 ONE 指令的控制器中。
您没有访问index.html 的控制器。

然后我修复了孩子之间的filter函数通信:

您在追加新的tree 时忘记传达过滤功能:

element.append('&lt;tree src="b" filter="filter({ object: object, isSelected: isSelected })"&gt;&lt;/tree&gt;');

此外,在您的父指令模板中,您还需要哈希将参数发送到函数:
filter="filter({ object: object, isSelected: isSelected })"

我编辑了您的 Plunker HERE,而没有使用我制作的上述 cmets 更改代码。
(我不确定你写的不是你想要的,因为你没有评论我宁愿不改变它,所以你仍然可以快速理解你的代码)
但是视图现在正在更新!
我认为用你想要的和上面的 cmets 进行一点调试就足够了。

编辑 2
您忘记返回具有属性chrilden 的对象。您返回了一个数组,这导致了问题。

function updateMyList(data) {
      var transformed = { children : data };
        $scope.myList = transformed;
    }

这是一个有效的PLUNKER

【讨论】:

    【解决方案2】:

    添加后尝试$scope.$apply()

    【讨论】:

    • 在 $scope.$apply() 或 $scope.$apply(function(){...}) 早期尝试过,因为该函数当前处于 $apply() 循环中.所以它不能调用一个新的
    • @ricrews 您可以通过在 $timeout 调用中包装对 $scope.$apply 的调用来避免 $apply 周期问题
    猜你喜欢
    • 2014-02-02
    • 2019-01-29
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    相关资源
    最近更新 更多