天道酬勤,学无止境

How can i apply a draggable directive to bootstrap modal using angularJS?

Question

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.

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.

UPDATE:

I have update the issue with Plunker

Issue: enter image description here

Answer1

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..

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

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

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

and the plunkr

Answer2

I upvoted @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 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!

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.

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.

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.

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({
    position : 'fixed',
});

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

.modal-header 
{
    cursor: move;
}

provides a complete solution.

Answer3

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.

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.

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));
Answer4

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

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

angular .module('app') .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 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);
      }
  };

} })();

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.

相关推荐
  • AngularUI modal to be draggable and resizable
    Question I have an angularUi modal window wrapped in a directive: html: <!doctype html> <html ng-app="plunker"> <head> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.js"></script> <script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.10.0.js"></script> <script src="main.js"></script> <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div my-modal="{ data: 'test2'}">test2</div> </body> </html> javascript: angular.module('plunker', ['ui.bootstrap', 'myModal']); angular.module("myModal", [])
  • make bootstrap twitter dialog modal draggable
    Question I'm trying to make bootstrap twitter dialog modal draggable with this jquery plugin: http://threedubmedia.com/code/event/drag#demos but it doesn't work. var $div = $('html'); console.debug($('.drag')); $('#modalTest') .drag("start", function(ev, dd) { dd.limit = $div.offset(); dd.limit.bottom = dd.limit.top + $div.outerHeight() - $(this).outerHeight(); dd.limit.right = dd.limit.left + $div.outerWidth() - $(this).outerWidth(); }) .drag(function(ev, dd) { $(this).css({ top: Math.min(dd.limit.bottom, Math.max(dd.limit.top, dd.offsetY)) , left: Math.min(dd.limit.right, Math.max(dd.limit
  • AngularJS reusable modal bootstrap directive
    Question I'm new with AngularJS. I'm trying to implement a reusable modal Bootstrap. This is the index.html: <div ng-controller="mymodalcontroller"> <modal lolo="modal1" modal-body='body' modal-footer='footer' modal-header='header' data-ng-click="myRightButton()"></modal> <a href="#{{modal1}}" role="button" class="btn btn-success" data-toggle="modal">Launch Demo Modal</a> </div> This is the module, controller and directive: var myModal = angular.module('myModal', []); myModal.controller('mymodalcontroller', function ($scope) { $scope.header = 'Put here your header'; $scope.body = 'Put here
  • Twitter Bootstrap Modal Form: How to drag and drop?
    Question I would like to be able to move around (on the greyed-out background, by dragging and dropping) the modal form that is provided by Bootstrap 2. Can anyone tell me what the best practice for achieving this is? Answer1 The bootstrap doesn't come with any dragging and dropping functionality by default, but you can add a little jQuery UI spice into the mix to get the effect you're looking for. For example, using the draggable interaction from the framework you can target your modal ID to allow it to be dragged around within the modal backdrop. Try this: JS $("#myModal").draggable({ handle
  • Simple Popup by using Angular JS
    Question I am new to using AngularJS. I have 4 buttons named Cancel, Deny, Success & Remove. If am I click on any button I want to show multiple message i.e. If I click on Cancel then message showing you click on Cancel button & so on. I have follow from below link but simple alert & no controller is used in this example. I want a A MODAL POPUP IN AN ANGULARJS DIRECTIVE example so I can understand. I have try to google it but no simple examples found. Please help. Thanks <!doctype html> <html ng-app="mymodal"> <body> <div ng-controller="MainCtrl" class="container"> <button ng-click=
  • Angularjs ui bootstrap: how to vertical center modal component?
    Question Recently I am learning angularjs. I have used bootstrap before. With jquery, I can easily change the position of the modal component position to make it vertical align. Now with angularjs, it seems not very easy to do that. Here is a plunker link of ui bootstrap modal, Does anyone know how to make it vertical align? ui bootstrap modal component 1.index.html <!doctype html> <html ng-app="ui.bootstrap.demo"> <head> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-animate.js"></script>
  • Updating attrs value inside directive - how to do it in AngularJS
    Question In a nutshell: I try to do something like this inside my directive - namely change the value of model that is liked to 'trigger' attribute: angular.element(element).on('hidden.bs.modal', function () { scope.$apply(function () { attrs.$set('trigger', null); }); }); and it does not work. Why? Should I do it other way around? And here is full story: I have a dialog that is triggered when showRemoveDialog flag is set. This flag is set when user clicks Remove button. Here is a dialog's opening tag: <div remove-dialog trigger="{{showRemoveDialog}}" class="modal fade" id="myModal"> Then I
  • Make bootstrap modal draggable and keep background usable
    Question I'm trying to make a bootstrap modal draggable anywhere on the page and when the modal is open, I want the user to be able to continue using the page. I was able to make the modal draggable with jquery-ui but I'm stuck on making the page usable while the modal is open. Tried several suggestions with CSS but none work quite as I'd like them to. This makes the page usable but the modal is limited to only a part of the page: Bootstrap Modal - make background clickable without closing out modal Same as this one: Enable background when open bootstrap modal popup Is it possible to achieve
  • AngularJS how to create a reusable template for Bootstrap modal
    Question So I am using the AngularJS Bootstrap modal (http://angular-ui.github.io/bootstrap/). Which is working fine but I was wondering if I could create a basic template that can take in a title and the content. Then it would populate my template with these info. The template would have a close button, cancel button, overlay, etc. Is there an easy way to do this AngularJS? This is taken from the example and it's about what I have. My content is in the templateUrl. It would be nice to pass in the modal template so I don't have to keep recreating the title and close buttons for every modal I
  • Create a simple Bootstrap Yes/No confirmation or just notification alert in AngularJS
    Question It's so simple in a non-Angular environment. Just html and a two line of js code to show a modal confirmation dialog on the screen. Now I am developting an AngularJS project in which I am using ui-bootstrap modal confirmation dialogs all over the place and I am sick of creating new controllers even for simple things like "Are you sure to delete this record?" kind of stuff. How do you handle these simple situations? I am sure some people wrote some directives to simplify the needs. I am asking you to share your experiences or the projects you know about that subject. Answer1 so create
  • Custom delete confirm in angularJS
    Question I have an angular app with a list of items. Im trying to implement a custom "Confirm Delete" modal, so that when the user clicks the "Delete" button beside an item, the modal open to confirm deletion. On clicking "Yes", a deleteItem() function is triggered. My problem is that the server is returning 404 not found for the delete request. It works when I use the standard jquery confirm dialog, so I'm guessing the item ID isn't getting passed through the modal to the delete function. can anyone help? <div class="span6"> <table class="table table-striped table-condensed"> <thead> <tr> <th
  • Modal Window Issue (Unknown Provider: ModalInstanceProvider)
    Question New to AngularJS and can't seem to find out what this error means. I've found a few others with the same error but it seems their issues don't correlate to mine. Unknown provider: $modalProvider <- $modal error with AngularJS (Seems I've got the latest ui-bootstrap version) And all of the others seem to be having scope issues with a modal, yet I can't seem to get the modal to begin with so I'm thinking these aren't related. Please tell me if I'm wrong and how that's the case: Scope issue in AngularJS using AngularUI Bootstrap Modal Scope issues with Angular UI modal I grabbed the ui
  • angular js opening modal after ajax call - not updaing html
    Question Making an ajax call after a button click and updating the html inside the modal via angular js. Sometime html inside modal is not updating. Tried $scope.$apply(); function but still its not updating sometimes. Provided the angular js and bootstrap code below . editfilterrules function is called after clicking on a button, $scope.editfilterrules=function(filterid,initid){ console.log('edit filter rules filterid:' + filterid + ' initid:' + initiativeid ); $http({ method : "GET", url : 'testoptions.php?filterid=' + filterid + '&initid=' + iniid }).then(function mySucces(response) { //
  • How to resize the image by dragging
    Question I have an image and need to resize it by dragging its corners, is there any options in angularjs? As of now I am doing manual image resize like this: <img src="~/Content/images/login-logo.png" width="{{image.width}}" height="{{image.size}}"/> <div class="right-wrapper"> <h3 class="right-head">Appearance <img src="~/Content/images/info-icon.png"></h3> <div ng-if="isImage"> <div ng-class="form-group"> size <input class="form-control" type="textbox" ng-model="image.size" /> </div> <div ng-class="form-group"> width <input class="form-control" type="textbox" ng-model="image.width" /> </div
  • angularjs: using a directive inside the ui-bootstrap modal
    Question I can't figure out how to call a directive from within a modal created with the $dialog service. That directive should also be able to see the buttons on the modal and override their ng-click action. Here's my modal template: <div class="modal-header"> <h1>Rechercher</h1> </div> <div class="modal-body"> <search-person></search-person> </div> <div class="modal-footer"> <button ng-click="close(result)" class="btn btn-primary">Close</button> </div> the searchPerson directive template: <span>{{test}}</span> the searchPerson directive itself: angular.module('person.directives').directive(
  • Invoking modal window in AngularJS Bootstrap UI using JavaScript
    Question Using the example mentioned here, how can I invoke the modal window using JavaScript instead of clicking a button? I am new to AngularJS and tried searching the documentation here and here without luck. Thanks Answer1 OK, so first of all the http://angular-ui.github.io/bootstrap/ has a <modal> directive and the $dialog service and both of those can be used to open modal windows. The difference is that with the <modal> directive content of a modal is embedded in a hosting template (one that triggers modal window opening). The $dialog service is far more flexible and allow you to load
  • What is $$phase in AngularJS?
    Question I found this code snippet which is part of a angular directive someone wrote for bootstrap modal. //Update the visible value when the dialog is closed //through UI actions (Ok, cancel, etc.) element.bind("hide.bs.modal", function () { scope.modalVisible = false; if (!scope.$$phase && !scope.$root.$$phase) scope.$apply(); }); I understood that this part is for the latter half of two way binding we bind to hide.bs.modal event and update modal when UI changes. I just wanted to know why is the person checking $$phase for scope and rootScope before calling apply ? Can't we straightaway
  • How to prepopulate a dialog in angularjs/bootstrap
    Question This question is in regards to angluarjs using bootstrap css and javascript. I have a list of items that I want to display and set up so that clicking on them opens a dialog to allow you to change the values. Something like this: <!-- The repeater !--> <ul> <li ng-repeat="item in items" ng-click="showDlg(item)"> {{item.text}} </li> </ul> <!-- The dialog !--> <div id="dlg" class="modal hide fade"> <div class="modal body"> <input id="title" type="text"> <button type="button">ok</button> </div> <div> The question is how do I implement the showDlg function to put up #dlg as a pop up
  • Angular tabs - sortable/moveable
    Question Are there any Angular JS Tabs directives that allow to reorder them (like a browser's tabs) If not a starting implementation would be great Using angular-ui-bootstap <tabset> <tab ng-repeat="tab in vm.tabs" active="tab.active" sortable-tab> </tab> <tab disabled="true" ng-click"vm.addNewTab()" class="nonSortable-addTab-plusButton"></tab> </tabset> How to make them reorderable? EDIT: Bounty added to use original tabset syntax above. Answer1 Using the Angular UI Bootstrap tabset, with just a sortable-tab directive: <tabset> <tab sortable-tab ng-repeat="tab in tabs" heading="{{tab.title}}
  • Closing Twitter Bootstrap Modal From Angular Controller
    Question I have a modal window that I use to present a form to users. They enter the information and then press a button the has an ng-click. The server processes the request and sends back a response. When the response is success I want to close the modal window from the controller. How can this be achieved? The modal is a partial included in another page Main page: <!-- main content --> <p>Foo</p> <!-- angular directive --> <foo-directive></foo-directive> Content of that directive: <div ng-controller="FooCtrl"> <ul class="thumbnails"> <li class="span3 tile tile-white" ng-repeat="foo in model