如何使用 angularJS 将可拖动指令应用于引导模式?

Posted

技术标签:

【中文标题】如何使用 angularJS 将可拖动指令应用于引导模式?【英文标题】:How can i apply a draggable directive to bootstrap modal using angularJS? 【发布时间】:2015-03-12 14:14:34 【问题描述】:

我在我的 Angular 应用程序中使用引导模式,它工作正常。我需要让它可拖动和调整大小,所以我定义了一个指令。现在的问题是它被应用于模态窗口内的内容,因此模态窗口变得透明。

如何在打开窗口时将可拖动指令分配给模态窗口? 这是代码,

HTML:

<div ng-controller="CustomWidgetCtrl">
    <div class="box-header-btns pull-right" style="top:10px" >
        <a title="settings" ng-click="openSettings(widget)"><i class="glyphicon glyphicon-cog"></i></a>
</div>
</div>

App.js:

var routerApp = angular.module('DiginRt',  ['ui.bootstrap','ngRoute']);
routerApp.controller('CustomWidgetCtrl', ['$scope', '$modal',
  function($scope, $modal) 

    $scope.openSettings = function(widget) 
          $modal.open(
            scope: $scope,
            templateUrl: 'chart_settings.html',
            controller: 'chartSettingsCtrl',        
            resolve: 
              widget: function() 
                return widget;
              
            
          );
        ;
    
    ])

图表设置是另一个 HTML 页面。这是我的 Draggable 指令。

更新:

我已经用Plunker更新了问题

问题:

【问题讨论】:

我无法将指令添加到模态中.. 但您可以看到这一点。 plnkr.co/edit/cu1TVKCY8ucYcT2AScJb?p=preview 太棒了。你是怎么做到的?你对现有的做了哪些改变? 【参考方案1】:

我找不到将指令添加到ui-bootstrap 打开的模态的方法,因为它用模态对话框包装了模板..

所以我所做的是使用以下代码将拖动事件设置到模态对话框本身(而不是指令)。

我知道将事件添加到指令内的另一个元素不是最佳做法,但在无法直接为元素设置指令的情况下也不是坏做法。

这样做的原因是因为ui-bootstrap 没有提供向modal.open 函数上的modal-dialog 添加指令的方法

这是要放在指令开头的代码:

element= angular.element(document.getElementsByClassName("modal-dialog"));

还有plunkr

【讨论】:

当我在原始应用程序中尝试时,它变得透明。 为此添加一些 CSS! 该指令似乎存在很多问题。甚至模态也会自动调整大小。你能检查一下这个plunker吗?我添加了一个单独的可拖动指令,名为 modaldraggable1 plnkr.co/edit/E8PzQNyyyvnvtwwcPksJ?p=preview 让我们continue this discussion in chat。【参考方案2】:

我赞成@Naeem_Shaikh 的回答,这基本上是对标准角度指令的改进。

但是,我已经能够修复标准 angular draggable 指令的另一个问题。

标准指令对整个对话框施加了可拖动性。一方面,这是我们想要的。我们要拖动整个对话框。但这有一个不幸的副作用,即在对话框中的各种编辑字段中单击不起作用:默认行为被阻止!不知何故,按钮已经在引导程序中被编码来克服这个问题,但不是文本编辑字段。我猜作者没有考虑我的用例。但对话框不仅仅是按钮!

这很棘手,因为需要拖动整个对话框,但您只希望通过单击标题“热点”中的单击来启动拖动。也就是说,发起拖拽的热点是被拖拽区域的一个子集。

Naeem 的修复使我能够让它工作,这样只有在标题中单击才会启动拖动。如果没有他的修复,坐标转换就会变得混乱。

function clickedWithinHeader(event) 
    var target = event.currentTarget;
    var hotspot = null;
    var hotspots = target.getElementsByClassName("modal-header");
    if (hotspots.length > 0) 
        hotspot = hotspots.item(0);
    
    if (hotspot !== null) 
        var eY = event.clientY;
        var tOT = target.offsetTop;
        var y = eY - tOT;
        var hH = hotspot.offsetHeight;
        // since the header occupies the full width across the top
        // no need to check X.  Note that this assumes the header
        // is on the top, which should be a safe assumption
        var within = (y <= hH);
        return within;
     else 
        return true;
    



// Draggable directive from: http://docs.angularjs.org/guide/compiler
// Modified so that only clicks in the dialog header trigger drag behavior.
// Otherwise clicking on dialog widgets did not give them focus.
angular.module('drag', []).directive('draggable', function($document) 
"use strict";
return function(scope, element) 
    var startX = 0, startY = 0, x = 0, y = 0;
    element= angular.element(document.getElementsByClassName("modal-dialog"));
    element.css(
        position : 'fixed',
        cursor : 'move',
    );
    element.on('mousedown', function(event) 
//      // OK, where did they touch?  Only want to do this
//      // when they clicked on the header.
        if (!clickedWithinHeader(event)) 
            return;
        
        // Prevent default dragging of selected content
        event.preventDefault();
        ...

请注意,clickedWithinHeader() 逻辑仅适用于 Naeem 的改进。 可能有更好的方法来做到这一点,但这是可行的。只有在标题中点击才会开始拖动,而在其他地方点击会做他们应该做的事情。

但是,这并不是全部答案,因为标准指令还将移动光标强加在整个对话框上,这非常令人不安,即使,或者特别是如果您无法拖动它出现的位置。

从指令中的 element.css 中删除它:

element.css(
    position : 'fixed',
);

并使用 CSS 将移动光标仅绑定到模态标题

.modal-header 

    cursor: move;

提供完整的解决方案。

【讨论】:

【参考方案3】:

我在这里合并了各种代码片段,并提出了一个更简单的解决方案。这个不依赖于检测点击事件是否发生在 modal-header 中,因为 mousedown 事件是专门绑定到 header 的。

此解决方案也不依赖于在整个 DOM 中搜索 modal-dialog 类,因为它在父级中搜索 modal-dialog 元素。这将允许您在屏幕上有多个对话框,并且只有一个是可拖动的。

我还创建了一个gist,该gist 进行了检查以防止对话框被移出可见范围。

(function (angular) 
    "use strict";

    angular.module('MyModule')
        .directive('modalDraggable', ['$document', modalDraggable]);

    function modalDraggable($document) 
        return function (scope, element) 
            var startX = 0,
                startY = 0,
                x = 0,
                y = 0;

            var draggable = angular.element(element.parents('.modal-dialog')[0]);

            draggable.find('.modal-header')
                .css('cursor', 'move')
                .on('mousedown', function (event) 
                    // Prevent default dragging of selected content
                    event.preventDefault();
                    startX = event.screenX - x;
                    startY = event.screenY - y;

                    $document.on('mousemove', mousemove);
                    $document.on('mouseup', mouseup);
                );

            function mousemove(event) 
                y = event.screenY - startY;
                x = event.screenX - startX;

                draggable.css(
                    top: y + 'px',
                    left: x + 'px'
                );
            

            function mouseup() 
                $document.unbind('mousemove', mousemove);
                $document.unbind('mouseup', mouseup);
            
        ;
    
(window.angular));

【讨论】:

【参考方案4】:

我将现有解决方案更改为屏幕上的多个模式。

HTML: ……

... // 模态页眉、正文、页脚。

JS调用模态: var modalInstance = $uibModal.open( 动画:真的, 控制器:'ModalAndamentoController', 控制器作为:'vm', windowClass: '中心模式', 尺寸:'lg', 模板网址:'modalAndamento.html' );

指令JS: (功能 () '使用严格';

角度 .module('app') .directive('draggable', draggableDirective);

/** @ngInject */ 函数 draggableDirective($document)

  //busca pelo elemento 
  var serachElement = function (parentElement, element) 
      //se o elemento pai corrente é igual ao elemento, então este foi encontrado
      if (parentElement == element[0]) 
          return true;
      

      //aprofunda mais um nível na árvore procurando pelo elemento
      var i = 0;
      for (i = 0; i < parentElement.childNodes.length; i++) 
          return serachElement(parentElement.childNodes[i], element);
      
      return false;
  ;

  //recupera o elemento referente ao dialog
  var getDialogFromElement = function (element) 
      //recupera todos os dialogs da tela
      var dialogs = document.getElementsByClassName("modal-dialog")
      //se tiver apenas um, então esse é o dialog procurado e o mesmo é retornado
      if (dialogs.length == 1) 
          return angular.element(dialogs[0]);
      
      //senão, varre todos os dialogs, procurando por aquele que contém o elemento corrente na sua árvore de elementos
      var i = 0;
      for (i = 0; i < dialogs.length; i++) 
          //se encontrar o elemento correte, então esse é o dialog procurado
          if (serachElement(dialogs[i], element)) 
              return angular.element(dialogs[i]);
                        
      
  ;

  //movimenta o dialog correspondente ao elemento informado (element)
  //O elemento que chega aqui é aquele que contém o atributo draggable
  return function (scope, element)           
      var startX = 0,
        startY = 0,
        x = 0,
        y = 0;

      //recupera o dialog correspondente ao element corrente
      element = getDialogFromElement(element);

      //coloca o cursor de movimento no header
      var header = angular.element(document.getElementsByClassName("modal-header"));
      header.css(              
          cursor: 'move'
      );          

      element.on('mousedown', function (event) 
          // Prevent default dragging of selected content
          //event.preventDefault();
          startX = event.screenX - x;
          startY = event.screenY - y;
          $document.on('mousemove', mousemove);
          $document.on('mouseup', mouseup);
      );

      function mousemove(event) 
          y = event.screenY - startY;
          x = event.screenX - startX;
          element.css(
              top: y + 'px',
              left: x + 'px'
          );
      

      function mouseup() 
          $document.unbind('mousemove', mousemove);
          $document.unbind('mouseup', mouseup);
      
  ;

)();

【讨论】:

以上是关于如何使用 angularJS 将可拖动指令应用于引导模式?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用angularJS突出显示可拖动元素下的拖放区?

如何使用 jQuery 将可拖动项目居中放置在可放置的图像地图区域上?

在被拖动后如何将可拖动对象保持在其位置?

如何将可拖动对象放入 gridster

如何将可拖动的 UIView 限制在特定范围内

如何将可拖动对象放置在目标对象的中心