AngularJS指令与ng-repeat - misbehavior共享范围

时间:2014-02-24 22:12:20

标签: jquery jquery-ui angularjs angularjs-directive angularjs-ng-repeat

我遇到了一种奇怪的情况。我需要有两个可排序的列表,它们应该通过拖放或添加/删除事件来交换元素。

我创建了一个运行良好的指令。控制器事件也做正确的工作。当组合方法时,问题就开始了(按钮添加+拖放+按钮再次添加按钮)。 KA-BOOM!

我把这个plnkr放在一起:http://plnkr.co/edit/DumufP1kDdkz1INAXwmF?p=preview

单击元素,然后单击按钮操作(添加/删除)。

让我分享一些指令的代码只是为了好玩,但请访问链接以查看整个实现。有关如何在plnkr

中重现该问题的更多信息
.directive('sortableList', function ($log) {
    return {
        restrict: 'A',
        scope: {
            fromList: '=',
            toList: '='
        },
        link: function (scope, elm, attrs) {                        

            var callback = {
                receive: function (event, ui) {

                    //-- Get the scope of the list-item
                    var scopeItem = ui.item.scope();
                    //-- Get new list index
                    var newIdx = ui.item.index();

                    //-- Find position in the list
                    var prevIdx = scope.fromList.indexOf(scopeItem.obj);                    

                    //-- Remove from source list
                    scope.fromList.splice(prevIdx, 1);
                    //-- Add to target list
                    if (newIdx >= scope.toList.length) {
                        scope.toList.push(scopeItem.obj);
                    }
                    else {
                        scope.toList.splice(newIdx, 0, scopeItem.obj);
                    }

                    //ui.item.removeClass('selectedSortListItem').addClass('sortListItem');

                    scope.$apply();
                },
                stop: function (event, ui) {
                    //$log.log(ui);
                }
            };            

            //-- Apply jquery ui sortable plug-in to element
            elm.sortable({
                handle: ".handle",
                connectWith: '.sortColumnsConnect',
                dropOnEmpty: true,
                cancel: ".ui-state-disabled",
                receive: callback.receive,
                stop: callback.stop
            }).disableSelection();

            //-- Sniff for list changes 
            /*scope.$watch(attrs.sortableList, function (newVal) {
                //-- Apply callback

                //if (angular.isUndefined(newVal)) return;

                elm.sortable('option', 'receive', callback.receive);

                if (!angular.isUndefined(attrs.trackSorting) && Boolean(attrs.trackSorting)) {
                    elm.sortable('option', 'stop', callback.stop);
                }
            });*/


        }
    }
})

非常感谢帮助。

1 个答案:

答案 0 :(得分:0)

我终于成功了。我已经完成了这项工作plunker。 我认为它与两个指令的范围有关(自定义+ ng-repeat),但事实证明我需要让ng-repeat执行整个工作并且永远不会删除ng-repeat注​​释,否则angular指令将制动。

虽然我的指令需要注意的一件事是$ destroy甚至,因为指令本身持有一个对象的引用,最好在页面离开时删除或者某些东西以避免内存泄漏情况。

现在,让我们在这里分享一些有趣的代码..再次..

.directive('sortableList', function ($log, $parse,$timeout) {
return {
    restrict: 'A',
    scope: {
        list: '='
    },
    link: function (scope, elm, attrs) {                        

        /*
         * We need to preserve a copy of ng-repeat comments
         * in order to not brake the ng directive. Lets made a copy and
         * and insert it back to the html list in the remove even.
         */
        var comments = elm.contents().filter(function(){ 
          return this.nodeType == 8;
        });

        var comment = undefined;
        if (comments.length > 0){
          comment = comments[0];
        }

        var callback = {
            start: function(event, ui){

              ui.item.sortable = {
                received: false,
                startIndex: ui.item.index()
              };

            },
            receive: function (event, ui) {
              ui.item.sortable.received = true;
            },
            update: function (event, ui) {
              //$log.log(elm);
              $log.log('update');

              var scopeItem = ui.item.scope();

              //-- Get new list index. Index in array not in html list
              var newIdx = ui.item.index(); 

              if (ui.item.sortable.received){

                $log.log('received');

                ui.sender.sortable('cancel');
                ui.item.sortable.received = false;
                //ui.item.sortable.doremove = true;

                scope.$apply(function(){

                  //-- Add to target list
                  if (newIdx >= scope.list.length) {
                    scope.list.push(scopeItem.obj);
                  }
                  else {
                    $log.log(newIdx);
                    scope.list.splice(newIdx, 0, scopeItem.obj);
                  }

                  ui.item.removeClass('selectedSortListItem').addClass('sortListItem');

                });
              }
              else {
                //-- Sort list
                if (ui.item.sortable.startIndex != newIdx){

                  $log.log('sort list');

                  scope.$apply(function(){

                    var idx = scope.list.indexOf(scopeItem.obj);

                    //-- end destroy
                    if (idx > -1){
                      scope.list.splice(idx, 1);  
                    }    
                    //-- Add to the new position
                    scope.list.splice(newIdx, 0, scopeItem.obj);

                  });
                }
              }


            },
            remove: function( event, ui ) {
              var scopeItem = ui.item.scope();

              /* Do the normal node removal  */
              //-- Seek
              var idx = scope.list.indexOf(scopeItem.obj);

              //-- end destroy
              if (idx > -1){
                scope.list.splice(idx, 1);  
              }

              /*
               * Insert back ng-repeat comments to the html list to avoid braking
               * the angular directive.
               */
              if (elm.children("li:not('.ui-state-disabled')").length === 0 && angular.isDefined(comment)){
                $log.log('insert comment');
                $log.log(comment);
                elm.append(comment);
                //$log.log(elm);
              }

              //$log.log('I have childrens: ' + elm.children("li:not('.ui-state-disabled')").length);
              //$log.log('remove me please at:' + idx);
            },
            stop: function (event, ui) {
              $log.log('stop');
            }
        };            

        scope.$watch('list.length', function() {
          // Timeout to let ng-repeat modify the DOM
          $timeout(function() {
            $log.log('epa!');
            //-- need to unselect those selected, otherwise Vishal will go like: Leo there is an error.. what? what? what?
            elm.children("li.selectedSortListItem").toggleClass('selectedSortListItem').toggleClass('sortListItem');
            elm.sortable('refresh');
          });
        });

        //-- Apply jquery ui sortable plug-in to element
        elm.sortable({
            handle: ".handle",
            connectWith: '.sortColumnsConnect',
            dropOnEmpty: true,
            cancel: ".ui-state-disabled",
            helper: "clone",
            start: callback.start,
            receive: callback.receive,
            update: callback.update,
            stop: callback.stop,
            remove: callback.remove
        }).disableSelection();



    }
}

})

看一下plunker,了解如何调用该指令及其目的。经过这么多的重新工厂后,我可能会忘记拆除一些东西......但现在似乎做得对了..至少它不像以前那样制动。

感谢。