AngularJS 指令与 ng-repeat 共享范围 - 不当行为 [英] AngularJS directive shares scope with ng-repeat - misbehavior

查看:22
本文介绍了AngularJS 指令与 ng-repeat 共享范围 - 不当行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm experiencing a weird situation. I need to have two sortable lists that should interchange elements by drag-n-drop or Add/Remove events.

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

I created a directive that works well. Also the controller event do the right job. The problem begins when methods are combined (button Add + drag-n-drop + button Add again). KA-BOOM!

我整理了这个 plnkr:http://plnkr.co/edit/DumufP1kDdkz1INAXwmF?p=预览

I put together this plnkr: http://plnkr.co/edit/DumufP1kDdkz1INAXwmF?p=preview

在单击按钮操作(添加/删除)之前单击元素.

Click on the elements before click the button action (Add/Remove).

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

Let me share some of the code of the directive just for fun but please visit the link to see the entire implementation. There is more information of how to reproduce the issue in the 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);
                }
            });*/


        }
    }
})

感谢帮助.

推荐答案

我终于成功了.我在这个 plunker 中完成了所有工作.我认为这与两个指令(自定义 + ng-repeat)的范围有关,但结果是我需要让 ng-repeat 完成整个工作并且永远不要删除 ng-repeat 注释,否则 angular 指令将制动.

I finally made it work. I have it all working in this plunker. I thought it was related to the scope of both directives (custom + ng-repeat) but it turned out to be that I needed to leave ng-repeat do the whole job and never remove the ng-repeat comments, otherwise the angular directive will brake.

尽管我的指令需要注意的一件事是 $destroy 甚至,因为指令本身持有一个对象的引用,当页面离开时最好稍后删除该对象的引用或为了避免内存泄漏情况.

One thing though that my directive needs to take care is the $destroy even, as the directive itself is holding a reference of an object that better is removed later when the page leaves or something in order to avoid a memory leak situation.

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

Now, let's share some code here for fun.. again..

.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 以了解指令的调用方式及其目的.我可能有一些东西在经过这么多次重新制造后忘记移除了..但似乎现在正在做正确的事情..至少它不像以前那样刹车了.

Take a look at the plunker to understand how the directive is invoked and the purpose of it. I might have some stuff that forgot to remove after so many re-factories.. but seems like now is doing the right thing.. at least it's not braking like it was before.

谢谢.

这篇关于AngularJS 指令与 ng-repeat 共享范围 - 不当行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆