【问题标题】:How to send a DOM function to a directive in AngularJS?如何将 DOM 函数发送到 AngularJS 中的指令?
【发布时间】:2015-01-10 15:05:58
【问题描述】:

我正在关注一个关于在 Angular 中实现拖放的精彩教程,我最初在 this StackOverflow answer 中找到了该教程,该教程链接到作者的博客和 GitHub 代码。

当我实现它并在我的项目中测试它时,我得到了错误:

[$parse:isecdom] Referencing DOM nodes in Angular expressions is disallowed! Expression: dropped(dragEl, dropEl)

According to Angular Docs,问题是我引用了一个函数,该函数将 DOM 对象作为 DOM 属性中的参数 - 它发生在这一行:

<div x-lvl-drop-target='true' x-on-drop='dropped(dragEl, dropEl)'>drop zone</div>

如您所见,on-drop 属性将这个dropped 函数(在我的控制器中定义)传递给指令lvlDropTarget(发布在下面),该指令调用它来执行拖放操作用户制作。 我喜欢这种设计,因为它使指令可重用于同一应用程序中的许多不同拖放可能性。本质上,我只需要在我的控制器中定义一个不同的函数并通过on-drop 属性将其传递给指令。

但是,不幸的是,这似乎已被 Angular 击落。有没有人知道如何实现同样的设计和功能,但在某种程度上 Angular 更适合?

这是lvlDropTarget 指令

module.directive('lvlDropTarget', ['$rootScope', 'uuid',
  function ($rootScope, uuid) {
    return {
      restrict: 'A',
      scope: {
        onDrop: '&'
      },
      link: function (scope, el, attrs, controller) {
        var id = angular.element(el).attr("id");
        if (!id) {
          id = uuid.new()
          angular.element(el).attr("id", id);
        }

        el.bind("dragover", function (e) {
          if (e.preventDefault) {
            e.preventDefault(); // Necessary. Allows us to drop.
          }

          if (e.stopPropagation) {
            e.stopPropagation();
          }

          e.dataTransfer.dropEffect = 'move';
          return false;
        });

        el.bind("dragenter", function (e) {
          angular.element(e.target).addClass('lvl-over');
        });

        el.bind("dragleave", function (e) {
          angular.element(e.target).removeClass('lvl-over'); // this / e.target is previous target element.
        });

        el.bind("drop", function (e) {
          if (e.preventDefault) {
            e.preventDefault(); // Necessary. Allows us to drop.
          }

          if (e.stopPropogation) {
            e.stopPropogation(); // Necessary. Allows us to drop.
          }

          var data = e.dataTransfer.getData("text");
          var dest = document.getElementById(id);
          var src = document.getElementById(data);

          scope.onDrop({
            dragEl: src,
            dropEl: dest
          });
        });

        $rootScope.$on("LVL-DRAG-START", function () {
          var el = document.getElementById(id);
          angular.element(el).addClass("lvl-target");
        });

        $rootScope.$on("LVL-DRAG-END", function () {
          var el = document.getElementById(id);
          angular.element(el).removeClass("lvl-target");
          angular.element(el).removeClass("lvl-over");
        });
      }
    }
  }
]);

【问题讨论】:

  • 我开始了赏金,因为我在互联网上找不到任何关于它的答案。
  • 你也在使用 jQuery。这可能是错误的原因。

标签: angularjs dom angularjs-directive parse-error


【解决方案1】:

10 月 14 日的问题是 logged on github

AngularJS 现在给出这个错误:“不允许在 Angular 表达式中引用 DOM 节点!”,因为:on-drop="controller.dropped(dragEl, dropEl)"。

已提出以下解决方案:

在较新版本的 AngularJS 中返回 DOM 元素本身会导致 $parse:isecdom 错误。很简单,让dropped(...) 回调处理获取元素。

因此,如果接受此更改,则将修改指令以返回拖放的元素 ID。然后由你的回调来做document.getElementById:

$scope.dropped = function(dragId, dropId) {
  var dragEl = document.getElementById(dragId);
  var dropEl = document.getElementById(dropId);

  console.log(dragEl, dropEl);
}

话虽如此,Angular 还是有充分的理由将 isecdom validation 添加到他们的 expression sandboxing 中:

AngularJS 限制从表达式中访问 DOM 节点,因为它是执行任意 Javascript 代码的已知方式。

此检查仅对 Angular 表达式中的对象索引和函数调用执行。这些是开发人员更难守卫的地方。点状成员访问(例如 a.b.c)不执行此检查 - 由开发人员决定不要直接在作用域链上公开这些敏感和强大的对象。

要解决此错误,请避免访问 DOM 节点。

比起试图绕过这种保护,我更喜欢你的选择 use ngDraggable - 它鼓励使用 declarative views 并且不需要 DOM manipulation in your controller。好电话。

【讨论】:

    【解决方案2】:

    我认为,您的问题的解决方案相当简单——使用中间对象包装dragEldropEl,如下所示:

    在链接功能中:

    scope.onDrop({ 
            event : {
              dragEl: src,
              dropEl: dest
            }
          });
    

    在 html 中:

    <div x-lvl-drop-target='true' x-on-drop='dropped(event)'>drop zone</div>
    

    在外面:

    $scope.dropped = function(event){
       console.log('dropped', event);
    }
    

    正在工作DEMO


    或者,您可以将有关拖动事件的信息保存在 value 中,如下所示:

    app.value('dropEvent', {dropEl:null, dragEl:null});
    
    app.directive('lvlDropTarget',
      function ($rootScope, dropEvent) {
        return {
    
     //... 
    
          link: function (scope, el, attrs, controller) {
    
     //...
    
            el.bind("dragover", function (e) {
    
     //... 
    
              dropEvent.dragEl = src;
              dropEvent.dropEl = dest;
    
              scope.onDrop();
            });
     //...
    
        }
      }
    );
    

    然后在控制器/外部上下文中使用它:

    app.controller('MainCtrl', function($scope, dropEvent) {
      $scope.dropped = function(){
        console.log('dropped', dropEvent);
      }
    });
    

    DEMO

    【讨论】:

    • 这些都是很好的建议。我最终使用ngDraggable 进行拖动,但这个答案中的信息仍然很有价值。我让克莱顿来决定这是否是公认的答案(如果他奖励你赏金)
    猜你喜欢
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    • 2018-12-01
    • 1970-01-01
    • 2015-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多