Created
May 26, 2018 12:55
-
-
Save ydeshayes/e09502c9ec13cf6ba463fae088d0cd27 to your computer and use it in GitHub Desktop.
Drag and drop directive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//This service and directive are a complete system of drag'n drop. This system drag and drop not only the html element | |
//but also the data of the ngModel where the draggable directive is added in the droppable ngModel | |
//The service is use to share the data between the two directives (draggable, droppable) | |
angular.module('dragndropServices', []).factory('dragndropService', function($rootScope) { | |
dragndropService = { | |
draggedData : {}, | |
setDraggedData : function(data){ | |
this.draggedData = data; | |
}, | |
getDraggedData : function(){ | |
return this.draggedData; | |
} | |
} | |
return dragndropService; | |
}).directive('draggable',['dragndropService', function(dragndropService) { | |
return{ | |
scope:{ | |
drag:'&', | |
ngModel:'=' | |
}, | |
link: function(scope, element, attrs) { | |
var OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/; | |
var REPEAT_REGEXP = /^\s*(.+)\s+in\s+(.*?)\s*\|(.*)\s*(\s+track\s+by\s+(.+)\s*)?$|^\s*(.+)\s+in\s+(.*?)\s*(.*)\s*(\s+track\s+by\s+(.+)\s*)?$/; | |
var el = element[0]; | |
//This function can extract the model from an ng-repeat or ng-options tag | |
var getModel = function(){ | |
if(attrs.ngRepeat){ | |
var model = attrs.ngRepeat; | |
var match = model.match(REPEAT_REGEXP); | |
if(match[2] !== undefined){ | |
return match[2]; | |
} | |
return match[8]; | |
}else if(attrs.ngOptions){ | |
var model = attrs.ngOptions; | |
var match = model.match(OPTIONS_REGEXP); | |
return match[7]; | |
} | |
return ""; | |
}; | |
var doOnDrag = function(bool){ | |
scope.$apply(function(scope) { | |
var dragFn = scope.drag(); | |
if (angular.isDefined(dragFn) && angular.isFunction(dragFn)) { | |
dragFn(bool); | |
} | |
}); | |
}; | |
element.addClass('draggable'); | |
el.draggable = true; | |
//We want all the inputs to keep they default highlight method | |
var inputs = el.querySelectorAll("input"); | |
for(var i=0;i<inputs.length;i++){ | |
inputs[i].addEventListener('focus', function(e) { | |
el.draggable = false; | |
}); | |
inputs[i].addEventListener('blur', function(e) { | |
el.draggable = true; | |
}); | |
} | |
inputs = undefined;//free the inputs array | |
el.addEventListener( | |
'dragstart', | |
function(e) { | |
doOnDrag(true); | |
e.dataTransfer.effectAllowed = 'move'; | |
e.dataTransfer.setData('Text', this.id);// Angular internal system | |
e.dataTransfer.setData('Model', getModel()); | |
dragndropService.setDraggedData(scope.ngModel); | |
this.classList.add('drag'); | |
return false; | |
}, | |
false | |
); | |
el.addEventListener( | |
'dragend', | |
function(e) { | |
console.log("drag end !!!"); | |
doOnDrag(false); | |
this.classList.remove('drag'); | |
return false; | |
}, | |
false | |
); | |
} | |
}}]).directive('droppable', ['dragndropService','$filter','$parse', function(dragndropService,$filter,$parse) { | |
return { | |
scope: { | |
drop: '&', // parent | |
model: '=ngModel', | |
dropFn: '=dropFn', | |
beforeDropFn: '=beforeDropFn', | |
dropItem: '=dropItem' | |
}, | |
link: function(scope, element, attrs) { | |
var el = element[0]; | |
el.addEventListener( | |
'dragover', | |
function(e) { | |
e.dataTransfer.dropEffect = 'move'; | |
//Allows us to drop | |
if (e.preventDefault) e.preventDefault(); | |
this.classList.add('over'); | |
return false; | |
}, | |
false | |
); | |
el.addEventListener( | |
'dragenter', | |
function(e) { | |
this.classList.add('over'); | |
return false; | |
}, | |
false | |
); | |
el.addEventListener( | |
'dragleave', | |
function(e) { | |
this.classList.remove('over'); | |
return false; | |
}, | |
false | |
); | |
el.addEventListener( | |
'drop', | |
function(e) { | |
// Stops some browsers from redirecting. | |
if (e.stopPropagation) e.stopPropagation(); | |
this.classList.remove('over'); | |
var draggedData = dragndropService.getDraggedData(); | |
//var test = e.dataTransfer.getData('Model'); | |
//push the data to the model and call the drop callback function | |
scope.$apply(function(scope) { | |
//var test = scope.dropFn; | |
//We check that the data is not already in the model | |
var alreadyInTheModel = (angular.isArray(scope.model) && scope.model.indexOf(draggedData) !== -1); | |
var beforeDropDataFn = scope.$parent.beforeDropData || scope.beforeDropFn; | |
if (!angular.isUndefined(beforeDropDataFn) && angular.isFunction(beforeDropDataFn)) { | |
draggedData = beforeDropDataFn(e, draggedData, attrs.ngModel, alreadyInTheModel); | |
} | |
if(angular.isArray(scope.model) && !alreadyInTheModel){ | |
scope.model.push(draggedData); | |
} | |
var dropFn = scope.$parent.drop || scope.dropFn; | |
if (!angular.isUndefined(dropFn) && angular.isFunction(dropFn)) { | |
dropFn(e, draggedData, scope.dropItem, scope.model, alreadyInTheModel); | |
} | |
}); | |
return false; | |
}, | |
false | |
); | |
} | |
} | |
}]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment