How can I apply a drag-and-drop directive to bootstrap modal using angularJS?

I use bootstrap modal in my Angular app, it works fine. I need to make it draggable and resizable, so I defined a directive. Now the problem is that it applies to the content inside the modal window, so the modal window becomes transparent.

How can I assign a draggable directive to a modal window when the window opens? 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 - another HTML page. Here is my Draggable directive.

UPDATE:

I have a problem with Plunker

Question: enter image description here

+5
source share
4 answers

I could not find a way to add the directive to the modal opened with ui-bootstrap , since it wraps the template with a modal dialog ..

So what I did was set the events to be dragged into the modal dialog itself (and not the directive) using the following code.

I know that it is not best to add events to another element inside the directive, but not to bad practice, and also in cases where you cannot set the directive directly to this element.

the reason is that ui-bootstrap does not provide a way to add a directive to the modal-dialog on modal.open

here is the code to put at the beginning of the directive:

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

and plunkr

+9
source

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

However, there is another problem with the standard angular draggable directive, which I was able to fix.

The standard directive imposes drag and drop on the entire dialog. On the one hand, this is what we want. We want to drag the whole dialogue. But this has an unfortunate side effect when clicks in different edit fields in the dialog box do not work: the default behavior is prevented! Somehow the buttons were encoded in the bootstrap to overcome this, but not the edit text fields. I assume that the authors did not consider my use case. But dialogs are more than just buttons!

This is difficult because you need to drag the entire dialog, but you only need to drag and drop, initiated by clicks in the heading "hot spot". In other words, the hot spot for initiating a drag is a subset of the area you want to drag.

Naeem's solution allowed me to make it work, so that only clicks in the header trigger a drag. Without its correction, the coordinate transformation was confused.

 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 clickedWithinHeader () logic only works with Naeem enhancement. This may be the best way to do this, but it works. Only clicks in the header trigger the drag and drop and clicks elsewhere do what they need to do.

However, this was not the whole answer, since the standard directive also superimposed the move cursor on the entire dialog, which is very confusing, even if, or especially if you cannot drag it to where it appeared.

Removing this element from element.css in the directive:

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

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

 .modal-header { cursor: move; } 

provides a complete solution.

+4
source

I have included various pieces of code here and come up with a simpler solution. It does not depend on the detection of a click event in the modal header, because the mousedown event is specifically bound to the header.

This solution also does not rely on searching the entire DOM for the modal dialog class, as it searches for parents for the modal dialog element. This will allow you to have multiple dialogs on the screen, and only one can be dragged.

I also created a gist that checks that the dialog does not move beyond the visible boundaries.

 (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)); 
+2
source

I am modifying an existing solution to more than one modal screen.

Html: ....

OR

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

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

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

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

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

}}) ();

0
source

Source: https://habr.com/ru/post/1211071/


All Articles