diff --git a/src/sortable.js b/src/sortable.js
index 3353f9d..7eb5e39 100644
--- a/src/sortable.js
+++ b/src/sortable.js
@@ -33,6 +33,10 @@ angular.module('ui.sortable', [])
return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display'));
}
+ function afterStop(e, ui) {
+ ui.item.sortable._destroy();
+ }
+
var opts = {};
// directive specific options
@@ -84,7 +88,10 @@ angular.module('ui.sortable', [])
// Save the starting position of dragged item
ui.item.sortable = {
+ model: ngModel.$modelValue[ui.item.index()],
index: ui.item.index(),
+ source: ui.item.parent(),
+ sourceModel: ngModel.$modelValue,
cancel: function () {
ui.item.sortable._isCanceled = true;
},
@@ -95,7 +102,12 @@ angular.module('ui.sortable', [])
return !!ui.item.sortable._isCustomHelperUsed;
},
_isCanceled: false,
- _isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed
+ _isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed,
+ _destroy: function () {
+ angular.forEach(ui.item.sortable, function(value, key) {
+ ui.item.sortable[key] = undefined;
+ });
+ }
};
};
@@ -136,7 +148,9 @@ angular.module('ui.sortable', [])
// the value will be overwritten with the old value
if(!ui.item.sortable.received) {
ui.item.sortable.dropindex = ui.item.index();
- ui.item.sortable.droptarget = ui.item.parent();
+ var droptarget = ui.item.parent();
+ ui.item.sortable.droptarget = droptarget;
+ ui.item.sortable.droptargetModel = droptarget.scope().$eval(droptarget.attr('ng-model'));
// Cancel the sort (let ng-repeat do the sort for us)
// Don't cancel if this is the received list because it has
@@ -262,6 +276,8 @@ angular.module('ui.sortable', [])
// call apply after stop
value = combineCallbacks(
value, function() { scope.$apply(); });
+
+ value = combineCallbacks(value, afterStop);
}
// wrap the callback
value = combineCallbacks(callbacks[key], value);
@@ -277,6 +293,9 @@ angular.module('ui.sortable', [])
angular.forEach(callbacks, function(value, key) {
opts[key] = combineCallbacks(value, opts[key]);
+ if( key === 'stop' ){
+ opts[key] = combineCallbacks(opts[key], afterStop);
+ }
});
} else {
diff --git a/test/sortable.e2e.callbacks.spec.js b/test/sortable.e2e.callbacks.spec.js
index 3902792..5be87d7 100644
--- a/test/sortable.e2e.callbacks.spec.js
+++ b/test/sortable.e2e.callbacks.spec.js
@@ -6,11 +6,12 @@ describe('uiSortable', function() {
beforeEach(module('ui.sortable'));
beforeEach(module('ui.sortable.testHelper'));
- var EXTRA_DY_PERCENTAGE, listContent;
+ var EXTRA_DY_PERCENTAGE, listContent, hasUndefinedProperties;
beforeEach(inject(function (sortableTestHelper) {
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
listContent = sortableTestHelper.listContent;
+ hasUndefinedProperties = sortableTestHelper.hasUndefinedProperties;
}));
describe('Callbacks related', function() {
@@ -188,6 +189,133 @@ describe('uiSortable', function() {
});
});
+ it('should properly set ui.item.sortable properties', function() {
+ inject(function($compile, $rootScope) {
+ var element, updateCallbackExpectations;
+ element = $compile('
')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ update: function(e, ui) {
+ if (ui.item.scope().item === 'Two') {
+ ui.item.sortable.cancel();
+ }
+ updateCallbackExpectations(ui.item.sortable);
+ }
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element);
+
+ $rootScope.$apply(function() {
+ });
+ var li = element.find(':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('Two');
+ expect(uiItemSortable.index).toEqual(1);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.items);
+ expect(uiItemSortable.isCanceled()).toBe(true);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+ };
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
+ expect($rootScope.items).toEqual(listContent(element));
+ updateCallbackExpectations = undefined;
+
+ li = element.find(':eq(0)');
+ dy = (2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('One');
+ expect(uiItemSortable.index).toEqual(0);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.items);
+ expect(uiItemSortable.isCanceled()).toBe(false);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+ };
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
+ expect($rootScope.items).toEqual(listContent(element));
+ updateCallbackExpectations = undefined;
+
+ li = element.find(':eq(2)');
+ dy = -(2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('One');
+ expect(uiItemSortable.index).toEqual(2);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.items);
+ expect(uiItemSortable.isCanceled()).toBe(false);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+ };
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
+ expect($rootScope.items).toEqual(listContent(element));
+ updateCallbackExpectations = undefined;
+
+ $(element).remove();
+ });
+ });
+
+ it('should properly free ui.item.sortable object', function() {
+ inject(function($compile, $rootScope) {
+ var element, uiItem, uiItemSortable_Destroy;
+ element = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ start: function (e, ui) {
+ uiItem = ui.item;
+ spyOn(ui.item.sortable, '_destroy').andCallThrough();
+ uiItemSortable_Destroy = ui.item.sortable._destroy;
+ },
+ update: function(e, ui) {
+ if (ui.item.scope().item === 'Two') {
+ ui.item.sortable.cancel();
+ }
+ }
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element);
+
+ var li = element.find(':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
+ expect($rootScope.items).toEqual(listContent(element));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+
+ li = element.find(':eq(0)');
+ dy = (2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
+ expect($rootScope.items).toEqual(listContent(element));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+
+ li = element.find(':eq(2)');
+ dy = -(2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
+ expect($rootScope.items).toEqual(listContent(element));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+ $(element).remove();
+ });
+ });
+
});
});
\ No newline at end of file
diff --git a/test/sortable.e2e.multi.spec.js b/test/sortable.e2e.multi.spec.js
index 860d349..a9cebae 100644
--- a/test/sortable.e2e.multi.spec.js
+++ b/test/sortable.e2e.multi.spec.js
@@ -6,13 +6,14 @@ describe('uiSortable', function() {
beforeEach(module('ui.sortable'));
beforeEach(module('ui.sortable.testHelper'));
- var EXTRA_DY_PERCENTAGE, listContent, listInnerContent, simulateElementDrag;
+ var EXTRA_DY_PERCENTAGE, listContent, listInnerContent, simulateElementDrag, hasUndefinedProperties;
beforeEach(inject(function (sortableTestHelper) {
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
listContent = sortableTestHelper.listContent;
listInnerContent = sortableTestHelper.listInnerContent;
simulateElementDrag = sortableTestHelper.simulateElementDrag;
+ hasUndefinedProperties = sortableTestHelper.hasUndefinedProperties;
}));
describe('Multiple sortables related', function() {
@@ -470,6 +471,222 @@ describe('uiSortable', function() {
});
});
+ it('should properly set ui.item.sortable properties', function() {
+ inject(function($compile, $rootScope) {
+ var elementTop, elementBottom, updateCallbackExpectations, stopCallbackExpectations;
+ elementTop = $compile('')($rootScope);
+ elementBottom = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.itemsTop = ['Top One', 'Top Two', 'Top Three'];
+ $rootScope.itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three'];
+ $rootScope.opts = {
+ connectWith: '.cross-sortable',
+ update: function(e, ui) {
+ if (ui.item.scope() &&
+ (typeof ui.item.scope().item === 'string') &&
+ ui.item.scope().item.indexOf('Two') >= 0) {
+ ui.item.sortable.cancel();
+ }
+ updateCallbackExpectations(ui.item.sortable);
+ },
+ stop: function(e, ui) {
+ stopCallbackExpectations(ui.item.sortable);
+ }
+ };
+ });
+
+ host.append(elementTop).append(elementBottom).append('');
+
+ var li1 = elementTop.find(':eq(1)');
+ var li2 = elementBottom.find(':eq(0)');
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('Top Two');
+ expect(uiItemSortable.index).toEqual(1);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.itemsTop);
+ expect(uiItemSortable.isCanceled()).toBe(true);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+
+ expect(uiItemSortable.dropindex).toEqual(1);
+ expect(uiItemSortable.droptarget.length).toBe(1);
+ expect(uiItemSortable.droptarget[0]).toBe(host.children()[1]);
+ expect(uiItemSortable.droptargetModel).toBe($rootScope.itemsBottom);
+ };
+ stopCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.received).toBe(true);
+ expect(uiItemSortable.moved).toBe(undefined);
+ };
+ simulateElementDrag(li1, li2, { place: 'below', extradx: -20, extrady: -10 });
+ expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ updateCallbackExpectations = stopCallbackExpectations = undefined;
+
+ li1 = elementBottom.find(':eq(1)');
+ li2 = elementTop.find(':eq(1)');
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('Bottom Two');
+ expect(uiItemSortable.index).toEqual(1);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[1]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.itemsBottom);
+ expect(uiItemSortable.isCanceled()).toBe(true);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+
+ expect(uiItemSortable.dropindex).toEqual(1);
+ expect(uiItemSortable.droptarget.length).toBe(1);
+ expect(uiItemSortable.droptarget[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.droptargetModel).toBe($rootScope.itemsTop);
+ };
+ stopCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.received).toBe(true);
+ expect(uiItemSortable.moved).toBe(undefined);
+ };
+ simulateElementDrag(li1, li2, { place: 'above', extradx: -20, extrady: -10 });
+ expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ updateCallbackExpectations = stopCallbackExpectations = undefined;
+
+ li1 = elementTop.find(':eq(0)');
+ li2 = elementBottom.find(':eq(0)');
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('Top One');
+ expect(uiItemSortable.index).toEqual(0);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.itemsTop);
+ expect(uiItemSortable.isCanceled()).toBe(false);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+
+ expect(uiItemSortable.dropindex).toEqual(1);
+ expect(uiItemSortable.droptarget.length).toBe(1);
+ expect(uiItemSortable.droptarget[0]).toBe(host.children()[1]);
+ expect(uiItemSortable.droptargetModel).toBe($rootScope.itemsBottom);
+ };
+ stopCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.received).toBe(true);
+ expect(uiItemSortable.moved).toBe('Top One');
+ };
+ simulateElementDrag(li1, li2, 'below');
+ expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Top One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ updateCallbackExpectations = stopCallbackExpectations = undefined;
+
+ li1 = elementBottom.find(':eq(1)');
+ li2 = elementTop.find(':eq(1)');
+ updateCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.model).toEqual('Top One');
+ expect(uiItemSortable.index).toEqual(1);
+ expect(uiItemSortable.source.length).toEqual(1);
+ expect(uiItemSortable.source[0]).toBe(host.children()[1]);
+ expect(uiItemSortable.sourceModel).toBe($rootScope.itemsBottom);
+ expect(uiItemSortable.isCanceled()).toBe(false);
+ expect(uiItemSortable.isCustomHelperUsed()).toBe(false);
+
+ expect(uiItemSortable.dropindex).toEqual(1);
+ expect(uiItemSortable.droptarget.length).toBe(1);
+ expect(uiItemSortable.droptarget[0]).toBe(host.children()[0]);
+ expect(uiItemSortable.droptargetModel).toBe($rootScope.itemsTop);
+ };
+ stopCallbackExpectations = function(uiItemSortable) {
+ expect(uiItemSortable.received).toBe(true);
+ expect(uiItemSortable.moved).toBe('Top One');
+ };
+ simulateElementDrag(li1, li2, { place: 'above', extradx: -20, extrady: -10 });
+ expect($rootScope.itemsTop).toEqual(['Top Two', 'Top One', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ updateCallbackExpectations = stopCallbackExpectations = undefined;
+
+ $(elementTop).remove();
+ $(elementBottom).remove();
+ });
+ });
+
+ it('should properly free ui.item.sortable object', function() {
+ inject(function($compile, $rootScope) {
+ var elementTop, elementBottom, uiItem, uiItemSortable_Destroy;
+ elementTop = $compile('')($rootScope);
+ elementBottom = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.itemsTop = ['Top One', 'Top Two', 'Top Three'];
+ $rootScope.itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three'];
+ $rootScope.opts = {
+ connectWith: '.cross-sortable',
+ start: function (e, ui) {
+ uiItem = ui.item;
+ spyOn(ui.item.sortable, '_destroy').andCallThrough();
+ uiItemSortable_Destroy = ui.item.sortable._destroy;
+ },
+ update: function(e, ui) {
+ uiItem.sortable = ui.item.sortable;
+ if (ui.item.scope() &&
+ (typeof ui.item.scope().item === 'string') &&
+ ui.item.scope().item.indexOf('Two') >= 0) {
+ ui.item.sortable.cancel();
+ }
+ }
+ };
+ });
+
+ host.append(elementTop).append(elementBottom).append('');
+
+ var li1 = elementTop.find(':eq(1)');
+ var li2 = elementBottom.find(':eq(0)');
+ simulateElementDrag(li1, li2, 'below');
+ expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+ li1 = elementBottom.find(':eq(1)');
+ li2 = elementTop.find(':eq(1)');
+ simulateElementDrag(li1, li2, { place: 'above', extradx: -20, extrady: -10 });
+ expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+ li1 = elementTop.find(':eq(0)');
+ li2 = elementBottom.find(':eq(0)');
+ simulateElementDrag(li1, li2, 'below');
+ expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Top One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+ li1 = elementBottom.find(':eq(1)');
+ li2 = elementTop.find(':eq(1)');
+ simulateElementDrag(li1, li2, { place: 'above', extradx: -20, extrady: -10 });
+ expect($rootScope.itemsTop).toEqual(['Top Two', 'Top One', 'Top Three']);
+ expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
+ expect($rootScope.itemsTop).toEqual(listContent(elementTop));
+ expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
+ expect(uiItemSortable_Destroy).toHaveBeenCalled();
+ expect(hasUndefinedProperties(uiItem.sortable)).toBe(true);
+ uiItem = uiItemSortable_Destroy = undefined;
+
+ $(elementTop).remove();
+ $(elementBottom).remove();
+ });
+ });
+
});
});
\ No newline at end of file
diff --git a/test/sortable.test-helper.js b/test/sortable.test-helper.js
index 2b5bf90..4da4d45 100644
--- a/test/sortable.test-helper.js
+++ b/test/sortable.test-helper.js
@@ -62,10 +62,20 @@ angular.module('ui.sortable.testHelper', [])
draggedElement.simulate('drag', dragOptions);
}
+ function hasUndefinedProperties(testObject) {
+ return testObject && Object.keys(testObject)
+ .filter(function(key) {
+ return testObject.hasOwnProperty(key) &&
+ testObject[key] !== undefined;
+ })
+ .length === 0;
+ }
+
return {
EXTRA_DY_PERCENTAGE: EXTRA_DY_PERCENTAGE,
listContent: listContent,
listInnerContent: listInnerContent,
- simulateElementDrag: simulateElementDrag
+ simulateElementDrag: simulateElementDrag,
+ hasUndefinedProperties: hasUndefinedProperties
};
});