`s and ``s.
-#### Attributes For Event Handling
+### Attributes For Event Handling
To handle events with html bindings just define any expression to listed event attributes.
If you defined an attribute for this events and defined callback function in sortableOptions at the same time, the attribute based callback will be called first.
@@ -161,7 +179,7 @@ $scope.sortableOptions = {
```
-#### Canceling
+### Canceling
Inside the `update` callback, you can check the item that is dragged and cancel the sorting.
diff --git a/src/sortable.js b/src/sortable.js
index 9b6bf4e..1076c85 100644
--- a/src/sortable.js
+++ b/src/sortable.js
@@ -62,6 +62,19 @@ angular.module('ui.sortable', [])
return null;
}
+ function setItemChildrenWidth(item) {
+ item.children().each(function() {
+ var $el = angular.element(this);
+
+ // Preserve the with of the element
+ $el.width($el.width());
+ });
+ }
+
+ function dummyHelper(e, item) {
+ return item;
+ }
+
function patchSortableOption(key, value) {
if (callbacks[key]) {
if( key === 'stop' ){
@@ -85,7 +98,8 @@ angular.module('ui.sortable', [])
return value;
}
- function patchUISortableOptions(newVal, oldVal, sortableWidgetInstance) {
+ function patchUISortableOptions(newOpts, oldOpts, sortableWidgetInstance) {
+
function addDummyOptionKey(value, key) {
if (!(key in opts)) {
// add the key in the opts object so that
@@ -100,11 +114,11 @@ angular.module('ui.sortable', [])
// update some options of the sortable
var optsDiff = null;
- if (oldVal) {
+ if (oldOpts) {
// reset deleted options to default
var defaultOptions;
- angular.forEach(oldVal, function(oldValue, key) {
- if (!newVal || !(key in newVal)) {
+ angular.forEach(oldOpts, function(oldValue, key) {
+ if (!newOpts || !(key in newOpts)) {
if (key in directiveOpts) {
if (key === 'ui-floating') {
opts[key] = 'auto';
@@ -129,16 +143,33 @@ angular.module('ui.sortable', [])
});
}
+ newOpts = angular.extend({}, newOpts);
// update changed options
- angular.forEach(newVal, function(value, key) {
- // if it's a custom option of the directive,
- // handle it approprietly
+ // handle the custom option of the directive first
+ angular.forEach(newOpts, function(value, key) {
if (key in directiveOpts) {
if (key === 'ui-floating' && (value === false || value === true) && sortableWidgetInstance) {
sortableWidgetInstance.floating = value;
}
+ if (key === 'ui-preserve-size' && (value === false || value === true)) {
+ var userProvidedHelper = opts.helper;
+ newOpts.helper = function(e, item) {
+ if (opts['ui-preserve-size'] === true) {
+ setItemChildrenWidth(item);
+ }
+ return (userProvidedHelper || dummyHelper).apply(this, arguments);
+ };
+ }
+
opts[key] = patchSortableOption(key, value);
+ }
+ });
+
+ // handle the normal option of the directive
+ angular.forEach(newOpts, function(value, key) {
+ if (key in directiveOpts) {
+ // the custom option of the directive are already handled
return;
}
@@ -197,7 +228,7 @@ angular.module('ui.sortable', [])
}
// thanks jquery-ui
- function isFloating (item) {
+ function isFloating(item) {
return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display'));
}
@@ -228,7 +259,8 @@ angular.module('ui.sortable', [])
// directive specific options
var directiveOpts = {
'ui-floating': undefined,
- 'ui-model-items': uiSortableConfig.items
+ 'ui-model-items': uiSortableConfig.items,
+ 'ui-preserve-size': undefined
};
var callbacks = {
@@ -480,6 +512,7 @@ angular.module('ui.sortable', [])
return function (e, item) {
var oldItemSortable = item.sortable;
var index = getItemIndex(item);
+
item.sortable = {
model: ngModel.$modelValue[index],
index: index,
@@ -504,12 +537,12 @@ angular.module('ui.sortable', [])
return inner;
};
- scope.$watchCollection('uiSortable', function(newVal, oldVal) {
+ scope.$watchCollection('uiSortable', function(newOpts, oldOpts) {
// ensure that the jquery-ui-sortable widget instance
// is still bound to the directive's element
var sortableWidgetInstance = getSortableWidgetInstance(element);
if (!!sortableWidgetInstance) {
- var optsDiff = patchUISortableOptions(newVal, oldVal, sortableWidgetInstance);
+ var optsDiff = patchUISortableOptions(newOpts, oldOpts, sortableWidgetInstance);
if (optsDiff) {
element.sortable('option', optsDiff);
diff --git a/test/sortable.e2e.directiveoptions.spec.js b/test/sortable.e2e.directiveoptions.spec.js
index 0b4b174..8fefffb 100644
--- a/test/sortable.e2e.directiveoptions.spec.js
+++ b/test/sortable.e2e.directiveoptions.spec.js
@@ -12,13 +12,15 @@ describe('uiSortable', function() {
beforeEach(module('ui.sortable'));
beforeEach(module('ui.sortable.testHelper'));
- var EXTRA_DY_PERCENTAGE, listContent, beforeLiElement, afterLiElement;
+ var EXTRA_DY_PERCENTAGE, listContent, beforeLiElement, afterLiElement, beforeTrElement, afterTrElement;
beforeEach(inject(function (sortableTestHelper) {
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
listContent = sortableTestHelper.listContent;
beforeLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeLiElement;
afterLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterLiElement;
+ beforeTrElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeTrElement;
+ afterTrElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterTrElement;
}));
tests.description = 'Custom directive options related';
@@ -32,6 +34,7 @@ describe('uiSortable', function() {
if (!useExtraElements) {
beforeLiElement = afterLiElement = '';
+ beforeTrElement = afterTrElement = '';
}
}));
@@ -393,6 +396,147 @@ describe('uiSortable', function() {
});
});
+ it('should work when the "ui-preserve-size" option is used', function() {
+ inject(function($compile, $rootScope) {
+ var width = '200px';
+ var element;
+ element = $compile(''.concat(
+ '',
+ ' | ',
+ '',
+ beforeTrElement,
+ '',
+ '{{ item }} | ',
+ ' ',
+ afterTrElement,
+ '',
+ ' '
+ ))($rootScope);
+
+ var itemsSelector = '.sortable-item';
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ 'ui-preserve-size': true,
+ stop: function(e, ui) {
+ expect(ui.item.children().css('width')).toEqual(width);
+ }
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element).append('');
+
+ var tr = element.find(itemsSelector + ':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * tr.outerHeight();
+ tr.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Three', 'Two']);
+ expect($rootScope.items).toEqual(listContent(element.find('tbody')).map($).map($.text));
+
+ tr = element.find(itemsSelector + ':eq(1)');
+ dy = -(1 + EXTRA_DY_PERCENTAGE) * tr.outerHeight();
+ tr.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['Three', 'One', 'Two']);
+ expect($rootScope.items).toEqual(listContent(element.find('tbody')).map($).map($.text));
+
+ $(element).remove();
+ });
+ });
+
+ it('should work when the "ui-preserve-size" option is false', function() {
+ inject(function($compile, $rootScope) {
+ var width = '200px';
+ var element;
+ element = $compile(''.concat(
+ '',
+ ' | ',
+ '',
+ beforeTrElement,
+ '',
+ '{{ item }} | ',
+ ' ',
+ afterTrElement,
+ '',
+ ' '
+ ))($rootScope);
+
+ var itemsSelector = '.sortable-item';
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ 'ui-preserve-size': false,
+ stop: function(e, ui) {
+ expect(ui.item.children().attr('style')).toEqual(undefined);
+ }
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element).append('');
+
+ var tr = element.find(itemsSelector + ':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * tr.outerHeight();
+ tr.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Three', 'Two']);
+ expect($rootScope.items).toEqual(listContent(element.find('tbody')).map($).map($.text));
+
+ tr = element.find(itemsSelector + ':eq(1)');
+ dy = -(1 + EXTRA_DY_PERCENTAGE) * tr.outerHeight();
+ tr.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['Three', 'One', 'Two']);
+ expect($rootScope.items).toEqual(listContent(element.find('tbody')).map($).map($.text));
+
+ $(element).remove();
+ });
+ });
+
+ it('should work when the "ui-preserve-size" & helper options are used', function() {
+ inject(function($compile, $rootScope) {
+ var width = '200px';
+ var element;
+ element = $compile(''.concat(
+ '',
+ ' | ',
+ '',
+ beforeTrElement,
+ '',
+ '{{ item }} | ',
+ ' ',
+ afterTrElement,
+ '',
+ ' '
+ ))($rootScope);
+
+ var itemsSelector = '.sortable-item';
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ 'ui-preserve-size': true,
+ helper: function (e, item) {
+ return item.clone();
+ },
+ stop: function(e, ui) {
+ expect(ui.item.children().css('width')).toEqual(width);
+ }
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element).append('');
+
+ var tr = element.find(itemsSelector + ':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * tr.outerHeight();
+ tr.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['One', 'Three', 'Two']);
+ expect($rootScope.items).toEqual(listContent(element.find('tbody')).map($).map($.text));
+
+ tr = element.find(itemsSelector + ':eq(1)');
+ dy = -(1 + EXTRA_DY_PERCENTAGE) * tr.outerHeight();
+ tr.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(['Three', 'One', 'Two']);
+ expect($rootScope.items).toEqual(listContent(element.find('tbody')).map($).map($.text));
+
+ $(element).remove();
+ });
+ });
+
}
[0, 1].forEach(function(useExtraElements){
diff --git a/test/sortable.test-helper.js b/test/sortable.test-helper.js
index 38721b4..6f5dd7b 100644
--- a/test/sortable.test-helper.js
+++ b/test/sortable.test-helper.js
@@ -102,6 +102,8 @@ angular.module('ui.sortable.testHelper', [])
extraElements: {
beforeLiElement: 'extra element',
afterLiElement: 'extra element',
+ beforeTrElement: ' |