caitp / angular-drop

Drag & Drop functionality in AngularJS, no jQuery required
http://caitp.github.io/angular-drop
158 stars 25 forks source link

Can we persist the position too? #48

Open sunflowerrahul opened 10 years ago

sunflowerrahul commented 10 years ago

Hi, Would like to persist the view at partcular position. When user change the position of the multiple view by drag and drop, it should be available at same postion. I am not able to create Pull request, so i created the request here.

caitp commented 10 years ago

@sunflowerrahul can you explain what you mean by "persist the view"? If possible, please provide an example of the "desirable" behaviour (using another library which enables it, if such a library exists)

sunflowerrahul commented 10 years ago

@caitp , we are looking to save the position of the view . or we can define some layout and save in the database ? I have no idea as such . In Liferay portal, they provide that feature(customization). They have used the alloy ui and bootstrap 2.3.2 css . I donnot whether they are storing the position in the database . My Requirement to customize the dashboard as per user level not the global level.

caitp commented 10 years ago

I'm still not sure what you're actually talking about =( visual example, please lol.

guisouza commented 10 years ago

@caitp, he is trying to persist the pins between sessions for example : I drag some element to some random position on the screen , when I come back to that page, it will be in that same position.

@sunflowerrahul this plugin, only provides the drag and drop behavior, you will have to manipulate the position and save it to the localStorage for example ..

if I have time i will send a pull request with that functionality

andrieski commented 9 years ago

Hi, I was curious to see if this feature was implemented? Is there now a way to retrieve the position of the element after its been dragged so that the position can be saved and restored at a later time? Thanks.

caitp commented 9 years ago

never got a PR for this. as @guisouza mentions, this is mainly about simple d&d, so it may be outside of the scope of the library. But I'm happy to review and possibly accept a PR that implements it

guisouza commented 9 years ago

uehuehueeuh I love "issues resurrections". btw . @andrieski you will have to create a function that stores the X and Y positions of the element and stores it to the localStorage for example, and attach it to the dragEnd event.

andrieski commented 9 years ago

Ok, I see, I'll try to do that. Also, I was wondering if this supports draggable handles? I was interested in a project that involved dragging elements and still being able to interact with the html inside. Or is that also outside the scope of the library?

caitp commented 9 years ago

well, there are other projects that try to do angular-y contenteditable stuff which would make sense for the inner html. You could certainly wrap a draggable around those (but pulling a contenteditable library into this and doing it here is probably a bit much, yeah)

andrieski commented 9 years ago

Hi, I was considering removing the 'draggable' and 'drag-keep-size' directive by watching a Boolean scope variable, but doing

element.removeAttr('draggable').removeAttr('drag-keep-size');

removes the visible attributes from chrome devtools, but the element is still draggable. I'd like to be able to turn the behavior on and off. What strategy would you recommend?

caitp commented 9 years ago

@andrieski to do that, you'd need to add some methods to the Draggable class to turn unlisten to the events, and possibly adjust its style/position. It should be pretty simple to implement. Patches welcome.

andrieski commented 9 years ago

@caitp thanks for the suggestions. JS is not my strong suit, and frankly, I don't know it too well. What do you mean by turn unlisten to the events? I attempted to add another option, which only works when the directive is compiled. That is, if I set allowDrag to false, the element won't drag, but if I later dynamically change allowDrag to true, dragging is still disabled. I'd definitely like to work on this, however. Should I make new issue or do you have any suggestions? I don't have too much experience with GitHub etiquette .

This is my failed attempt:

var draggableOptions = {
  'dragKeepSize': 'keepSize',
  'dragWithin': 'dragWithin',
  'allowDrag': 'allowDrag'
};

      dragStart: function(event) {
        var self = isDraggable(this) ? this : $drag.draggable(this);
        if (self.options.allowDrag == false) {
          return;
        }
        event = fixup(event);
        event.preventDefault();
caitp commented 9 years ago

those options are only handled at compile time, because the main api is the Draggable class and $drag service. You can add a method for turning dragging on and off, and do it by removing/re-adding event listeners (among other things). Then just wrap that method with a declarative api in the directive if you want to

andrieski commented 9 years ago

Thanks, I have a feint idea of where to get started, but I'm not too familiar with event listeners. Could you provide an example of an even listener I would have to add or remove and where? I think that will help me get started!

caitp commented 9 years ago
// jQuery / jqLite
// first parameter is a space separated list of event names, second param is a
// function. jQuery / jqLite keep lists of registered events, so it's easy to
// unregister all of them to prevent memory leaks.
//
// There's an optional 2nd argument for providing a selector, which is useful for
// event delegation --- however jqLite (part of angular) does not support this.
element.
  on('event-name', eventListenerFunc).
  off('event-name', eventListenerFunc);

// DOM --- first param is event name, 2nd is a function, and optionally a
// 3rd parameter is a boolean determining if listener is capturing.
// Capturing listeners fire before listeners below in the DOM, so they
// change the way event bubbling works.
node.addEventListener('event', eventListenerFunc);
node.removeEventLister('event', eventListenerFunc);

// DOM for old IE --- Event name is preceded with 'on', there is no optional useCapture argument
node.attachEvent('onevent', eventListenerFunc);
node.detachEvent('onevent', eventListenerFunc);

More info:

andrieski commented 9 years ago

Hi, thanks for the help. So I believe I understand event listeners, but I'm not sure where I would call the method and how I can give it access to the allowDrag attribute if it gets updated dynamically. Did you mean to put my method in the $dragProvider function? So far, I checked dragStart, and when I update the allowDrag attribute, the following line still returns the initial value:

console.log(self.options.allowDrag);

Adding this method in $dragProvider doesn't work because options.allowDrag doesn't exist, and either way, having the method in $dragProvider doesn't give it the ability to see the updated value.

       addRemoveDrag: function(element, options) {
           element = angular.element(element);

           var $draggable = element.data("$draggable");

           options = angular.extend({
             delay: 500,
             distance: 1
           }, options || {});

          if (options.allowDrag == true) {
            element.on("mousedown", $draggable.dragStart);
          } else{
            element.off("mousedown", $draggable.dragStart);
          }
        },

Even checking the drag function with console.log(self.options);, my variable isn't being updated. So I'm just unsure of where I can detect any changed in the attribute value...?

Currently, I added this directive and attribute: allow-drag="{{allow}}"

And in my controller, I have $scope.allow = true;

And finally a button that toggles the allow variable. Also, I'm not sure what "wrap that method with a declarative api in the directive" means, but could this be the key?

caitp commented 9 years ago

The $drag and Draggable class should not care about any attributes at all, so they shouldn't be watching the DOM. Instead they use event listeners and internal state.

The directives are places where you could care about the DOM, so you could do something like this:

// In the directive:
if (attrs.isPinned) {
  var isPinned = false;
  $scope.$watch(attrs.isPinned, function(val) {
    val = !!val;
    if (isPinned !== val) {
      isPinned = val;
      $drag.draggable(element).setPinned(isPinned);

      // setPinned would listen or unlisten to the events, cancel pending drop events, etc
    }
  });
}

The isPinned stuff is a bit more complicated than it needs to be, you could do something more clever there like this:

attrs.isPinned = attrs.isPinned.replace(/^((?:\s*:)?)([^]+)$/,
    function(match, bindOnce, expression) {
  // Make sure the watched value will only trigger the watcher when boolean value changes
  return bindOnce + '!!(' + expression + ')';
});
$scope.$watch(attrs.isPinned, function(booleanVal) {
    $drag.draggable(element).setPinned(booleanVal);
});
andrieski commented 9 years ago

Thanks, I will try this and a few other things. I think my problem is that I have to review a few things first, like angular providers and try to understand the overall structure of the module, so I'll work on that first.