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

时间:2021-12-27 03:05:32

I am using bootstrap modal in my Angular Application, it works fine. I need to make it draggable and resizeable, so i have defined a directive. The issue now is it getting applied to the content inside the modal window, hence the modal window becomes transparent.

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

how can i assign the draggable directive to the modal window when opening the window? Here is the code,

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

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;
              }
            }
          });
        };
    }
    ])

Chart Settings is another HTML page. Here is my Draggable directive.

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

UPDATE:

I have update the issue with Plunker

我已经用Plunker更新了这个问题

Issue: 如何使用angularJS将可拖动指令应用于bootstrap模式?

4 个解决方案

#1


9  

I couldn't find a way to add the directive to the modal opened by ui-bootstrap, as it wraps the template with a modal-dialog..

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

So what i did is setting the events for drag to the modal-dialog itself(not the directive) using the following code.

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

I know it is not the best practice to add events to another element inside a directive but not a bad practice as well in cases like these, where you cant set a directive directly to the element.

我知道将事件添加到指令中的另一个元素并不是最好的做法,但在这样的情况下也不是坏习惯,在这种情况下,您无法直接向元素设置指令。

the reason doing this is because ui-bootstrap doesnt provide a way to add a directive to the modal-dialog on modal.open function

这样做的原因是因为ui-bootstrap没有提供一种方法来向modal.open函数的模态对话框添加指令

here is the code to be put at the start of the directive:

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

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

and the plunkr

和傻瓜

#2


4  

I upvoted @Naeem_Shaikh's answer which is basically an improvement on the standard angular directive.

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

However, there is another problem with the standard angular draggable directive that I have been able to fix.

但是,我能够解决的标准角度可拖动指令存在另一个问题。

The standard directive imposes draggability on the whole dialog. On the one hand, this is what we want. We want to drag the whole dialog. But this has unfortunate side effect that clicks in various edit fields in the dialog do not work: the default behavior is prevented! Somehow buttons have been coded in bootstrap to overcome this but not text edit fields. I'm guessing that the authors weren't considering my use case. But dialogs are more than just buttons!

标准指令在整个对话框中强制执行可拖动性。一方面,这就是我们想要的。我们想拖动整个对话框。但这有一个令人遗憾的副作用,即对话框中各种编辑字段的点击不起作用:默认行为被阻止!不知何故,按钮已在bootstrap中编码以克服此问题,但不是文本编辑字段。我猜这些作者没有考虑我的用例。但对话不仅仅是按钮!

It's tricky since it is the whole dialog that needs to be dragged, but you only want drags initiated by clicks in the header "hotspot". In other words, the hotspot to initiate dragging is a subset of the area to be dragged.

这很棘手,因为它是需要拖动的整个对话框,但您只需要通过标题“hotspot”中的点击启动拖动。换句话说,启动拖动的热点是要拖动的区域的子集。

Naeem's fix enabled me to get it to work so that only clicks in the header initiated drags. Without his fix, the coordinate conversion was getting confused.

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();
        ...

Note that the clickedWithinHeader() logic only works with Naeem's improvement. There may be a better way to do it, but this works. Only clicks in the header initiate dragging and clicks elsewhere do what they're supposed to do.

请注意,clickedWithinHeader()逻辑仅适用于Naeem的改进。可能有更好的方法,但这是有效的。只有标题中的点击才会启动拖动并点击其他位置执行他们应该执行的操作。

However, that wasn't the whole answer since the standard directive also imposed the move cursor over the whole dialog which is very disconcerting, even if, or especially if you can't drag where it appears.

然而,这不是整个答案,因为标准指令也将移动光标强加在整个对话框上,这非常令人不安,即使,或者特别是如果你不能拖动它出现的地方。

Removing that from the element.css in the directive:

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

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

and tying the move cursor only to the modal header using CSS

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

.modal-header 
{
    cursor: move;
}

provides a complete solution.

提供完整的解决方案。

#3


2  

I incorporated the various code fragments here and came up with a simpler solution. This one doesn't rely on detecting if the click event happened in the modal-header, because the mousedown event is bound to the header specifically.

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

This solution also doesn't rely on searching the entire DOM for the modal-dialog class, as it searches the parents for the modal-dialog element. This will allow you to have multiple dialogs on screen and only one be draggable.

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

I also created a gist that has checking to prevent the dialog from being moved outside the visible bounds.

我还创建了一个要点,以防止对话框被移动到可见边界之外。

(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


0  

I change an existing solution to more than one modal on screen.

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

Html: ....

OR

... // modal header, body, footer.

JS call modal: var modalInstance = $uibModal.open({ animation: true, controller: 'ModalAndamentoController', controllerAs: 'vm', windowClass: 'center-modal', size: 'lg', templateUrl: 'modalAndamento.html'
});

JS调用模式:var modalInstance = $ uibModal.open({animation:true,controller:'ModalAndamentoController',controllerAs:'vm',windowClass:'center-modal',size:'lg',templateUrl:'modalAndamento.html' });

Directive JS: (function () { 'use strict';

指令JS :( function(){'use strict';

angular .module('app') .directive('draggable', draggableDirective);

angular .module('app')。direire('draggable',draggableDirective);

/** @ngInject */ function draggableDirective($document) {

/ ** @ngInject * / function 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);
      }
  };

} })();

#1


9  

I couldn't find a way to add the directive to the modal opened by ui-bootstrap, as it wraps the template with a modal-dialog..

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

So what i did is setting the events for drag to the modal-dialog itself(not the directive) using the following code.

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

I know it is not the best practice to add events to another element inside a directive but not a bad practice as well in cases like these, where you cant set a directive directly to the element.

我知道将事件添加到指令中的另一个元素并不是最好的做法,但在这样的情况下也不是坏习惯,在这种情况下,您无法直接向元素设置指令。

the reason doing this is because ui-bootstrap doesnt provide a way to add a directive to the modal-dialog on modal.open function

这样做的原因是因为ui-bootstrap没有提供一种方法来向modal.open函数的模态对话框添加指令

here is the code to be put at the start of the directive:

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

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

and the plunkr

和傻瓜

#2


4  

I upvoted @Naeem_Shaikh's answer which is basically an improvement on the standard angular directive.

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

However, there is another problem with the standard angular draggable directive that I have been able to fix.

但是,我能够解决的标准角度可拖动指令存在另一个问题。

The standard directive imposes draggability on the whole dialog. On the one hand, this is what we want. We want to drag the whole dialog. But this has unfortunate side effect that clicks in various edit fields in the dialog do not work: the default behavior is prevented! Somehow buttons have been coded in bootstrap to overcome this but not text edit fields. I'm guessing that the authors weren't considering my use case. But dialogs are more than just buttons!

标准指令在整个对话框中强制执行可拖动性。一方面,这就是我们想要的。我们想拖动整个对话框。但这有一个令人遗憾的副作用,即对话框中各种编辑字段的点击不起作用:默认行为被阻止!不知何故,按钮已在bootstrap中编码以克服此问题,但不是文本编辑字段。我猜这些作者没有考虑我的用例。但对话不仅仅是按钮!

It's tricky since it is the whole dialog that needs to be dragged, but you only want drags initiated by clicks in the header "hotspot". In other words, the hotspot to initiate dragging is a subset of the area to be dragged.

这很棘手,因为它是需要拖动的整个对话框,但您只需要通过标题“hotspot”中的点击启动拖动。换句话说,启动拖动的热点是要拖动的区域的子集。

Naeem's fix enabled me to get it to work so that only clicks in the header initiated drags. Without his fix, the coordinate conversion was getting confused.

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();
        ...

Note that the clickedWithinHeader() logic only works with Naeem's improvement. There may be a better way to do it, but this works. Only clicks in the header initiate dragging and clicks elsewhere do what they're supposed to do.

请注意,clickedWithinHeader()逻辑仅适用于Naeem的改进。可能有更好的方法,但这是有效的。只有标题中的点击才会启动拖动并点击其他位置执行他们应该执行的操作。

However, that wasn't the whole answer since the standard directive also imposed the move cursor over the whole dialog which is very disconcerting, even if, or especially if you can't drag where it appears.

然而,这不是整个答案,因为标准指令也将移动光标强加在整个对话框上,这非常令人不安,即使,或者特别是如果你不能拖动它出现的地方。

Removing that from the element.css in the directive:

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

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

and tying the move cursor only to the modal header using CSS

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

.modal-header 
{
    cursor: move;
}

provides a complete solution.

提供完整的解决方案。

#3


2  

I incorporated the various code fragments here and came up with a simpler solution. This one doesn't rely on detecting if the click event happened in the modal-header, because the mousedown event is bound to the header specifically.

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

This solution also doesn't rely on searching the entire DOM for the modal-dialog class, as it searches the parents for the modal-dialog element. This will allow you to have multiple dialogs on screen and only one be draggable.

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

I also created a gist that has checking to prevent the dialog from being moved outside the visible bounds.

我还创建了一个要点,以防止对话框被移动到可见边界之外。

(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


0  

I change an existing solution to more than one modal on screen.

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

Html: ....

OR

... // modal header, body, footer.

JS call modal: var modalInstance = $uibModal.open({ animation: true, controller: 'ModalAndamentoController', controllerAs: 'vm', windowClass: 'center-modal', size: 'lg', templateUrl: 'modalAndamento.html'
});

JS调用模式:var modalInstance = $ uibModal.open({animation:true,controller:'ModalAndamentoController',controllerAs:'vm',windowClass:'center-modal',size:'lg',templateUrl:'modalAndamento.html' });

Directive JS: (function () { 'use strict';

指令JS :( function(){'use strict';

angular .module('app') .directive('draggable', draggableDirective);

angular .module('app')。direire('draggable',draggableDirective);

/** @ngInject */ function draggableDirective($document) {

/ ** @ngInject * / function 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);
      }
  };

} })();