diff --git a/src/sortable.js b/src/sortable.js index 400ee15..85f1f74 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -11,8 +11,8 @@ angular.module('ui.sortable', []) return { require: '?ngModel', scope: { - ngModel: '=ngModel', - uiSortable: '=uiSortable' + ngModel: '=', + uiSortable: '=' }, link: function(scope, element, attrs, ngModel) { var savedNodes; @@ -47,6 +47,18 @@ angular.module('ui.sortable', []) return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display')); } + function getElementScope(elementScopes, element) { + var result = null; + for (var i = 0; i < elementScopes.length; i++) { + var x = elementScopes[i]; + if (x.element[0] === element[0]) { + result = x.scope; + break; + } + } + return result; + } + function afterStop(e, ui) { ui.item.sortable._destroy(); } @@ -126,7 +138,7 @@ angular.module('ui.sortable', []) }; }; - callbacks.activate = function(/*e, ui*/) { + callbacks.activate = function(e, ui) { // We need to make a copy of the current element's contents so // we can restore it after sortable has messed it up. // This is inside activate (instead of start) in order to save @@ -151,10 +163,20 @@ angular.module('ui.sortable', []) // exact match with the placeholder's class attribute to handle // the case that multiple connected sortables exist and // the placehoilder option equals the class of sortable items - var excludes = element.find('[class="' + phElement.attr('class') + '"]'); + var excludes = element.find('[class="' + phElement.attr('class') + '"]:not([ng-repeat], [data-ng-repeat])'); savedNodes = savedNodes.not(excludes); } + + // save the directive's scope so that it is accessible from ui.item.sortable + var connectedSortables = ui.item.sortable._connectedSortables || []; + + connectedSortables.push({ + element: element, + scope: scope + }); + + ui.item.sortable._connectedSortables = connectedSortables; }; callbacks.update = function(e, ui) { @@ -165,8 +187,9 @@ angular.module('ui.sortable', []) ui.item.sortable.dropindex = ui.item.index(); var droptarget = ui.item.parent(); ui.item.sortable.droptarget = droptarget; - var attr = droptarget.attr('ng-model') || droptarget.attr('data-ng-model'); - ui.item.sortable.droptargetModel = droptarget.scope().$eval(attr); + + var droptargetScope = getElementScope(ui.item.sortable._connectedSortables, droptarget); + ui.item.sortable.droptargetModel = droptargetScope.ngModel; // Cancel the sort (let ng-repeat do the sort for us) // Don't cancel if this is the received list because it has diff --git a/test/sortable.e2e.callbacks.spec.js b/test/sortable.e2e.callbacks.spec.js index 50b182b..ae4ceb5 100644 --- a/test/sortable.e2e.callbacks.spec.js +++ b/test/sortable.e2e.callbacks.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper')); @@ -35,7 +41,7 @@ describe('uiSortable', function() { $rootScope.$apply(function() { $rootScope.opts = { update: function(e, ui) { - if (ui.item.scope().item === 'Two') { + if (ui.item.sortable.model === 'Two') { ui.item.sortable.cancel(); } } @@ -77,7 +83,7 @@ describe('uiSortable', function() { return item; }, update: function(e, ui) { - if (ui.item.scope().item === 'Two') { + if (ui.item.sortable.model === 'Two') { ui.item.sortable.cancel(); } } @@ -117,7 +123,7 @@ describe('uiSortable', function() { $rootScope.$apply(function() { $rootScope.opts = { update: function(e, ui) { - $rootScope.logs.push('Moved element ' + ui.item.scope().item); + $rootScope.logs.push('Moved element ' + ui.item.sortable.model); } }; $rootScope.items = ['One', 'Two', 'Three']; @@ -148,7 +154,7 @@ describe('uiSortable', function() { $rootScope.$apply(function() { $rootScope.opts = { stop: function(e, ui) { - $rootScope.logs.push('Moved element ' + ui.item.scope().item); + $rootScope.logs.push('Moved element ' + ui.item.sortable.model); } }; $rootScope.items = ['One', 'Two', 'Three']; @@ -199,7 +205,7 @@ describe('uiSortable', function() { $rootScope.$apply(function() { $rootScope.opts = { update: function(e, ui) { - if (ui.item.scope().item === 'Two') { + if (ui.item.sortable.model === 'Two') { ui.item.sortable.cancel(); } updateCallbackExpectations(ui.item.sortable); @@ -276,7 +282,7 @@ describe('uiSortable', function() { uiItemSortable_Destroy = ui.item.sortable._destroy; }, update: function(e, ui) { - if (ui.item.scope().item === 'Two') { + if (ui.item.sortable.model === 'Two') { ui.item.sortable.cancel(); } } diff --git a/test/sortable.e2e.directiveoptions.spec.js b/test/sortable.e2e.directiveoptions.spec.js index bd2461c..9eba4b8 100644 --- a/test/sortable.e2e.directiveoptions.spec.js +++ b/test/sortable.e2e.directiveoptions.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper')); diff --git a/test/sortable.e2e.directives.spec.js b/test/sortable.e2e.directives.spec.js index 1c071a4..3292944 100644 --- a/test/sortable.e2e.directives.spec.js +++ b/test/sortable.e2e.directives.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper')); diff --git a/test/sortable.e2e.multi.spec.js b/test/sortable.e2e.multi.spec.js index efaed48..bf2c0e0 100644 --- a/test/sortable.e2e.multi.spec.js +++ b/test/sortable.e2e.multi.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper')); @@ -68,14 +74,17 @@ describe('uiSortable', function() { inject(function($compile, $rootScope) { var elementTop, elementBottom, wrapperTop, wrapperBottom, + wrapperTopScope, wrapperBottomScope, itemsTop, itemsBottom; - wrapperTop = $compile('
')($rootScope); - wrapperBottom = $compile('
')($rootScope); + wrapperTopScope = $rootScope.$new(); + wrapperBottomScope = $rootScope.$new(); + wrapperTop = $compile('
')(wrapperTopScope); + wrapperBottom = $compile('
')(wrapperBottomScope); host.append(wrapperTop).append(wrapperBottom).append('
'); $rootScope.$apply(function() { - wrapperTop.scope().itemsTop = itemsTop = ['Top One', 'Top Two', 'Top Three']; - wrapperBottom.scope().itemsBottom = itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three']; + wrapperTopScope.itemsTop = itemsTop = ['Top One', 'Top Two', 'Top Three']; + wrapperBottomScope.itemsBottom = itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three']; $rootScope.opts = { connectWith: '.cross-sortable' }; }); @@ -462,9 +471,9 @@ describe('uiSortable', function() { $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) { + if (ui.item.sortable.model && + (typeof ui.item.sortable.model === 'string') && + ui.item.sortable.model.indexOf('Two') >= 0) { ui.item.sortable.cancel(); } } @@ -521,9 +530,9 @@ describe('uiSortable', function() { $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) { + if (ui.item.sortable.model && + (typeof ui.item.sortable.model === 'string') && + ui.item.sortable.model.indexOf('Two') >= 0) { ui.item.sortable.cancel(); } updateCallbackExpectations(ui.item.sortable); @@ -660,9 +669,9 @@ describe('uiSortable', function() { $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) { + if (ui.item.sortable.model && + (typeof ui.item.sortable.model === 'string') && + ui.item.sortable.model.indexOf('Two') >= 0) { ui.item.sortable.cancel(); } updateCallbackExpectations(ui.item.sortable); @@ -729,21 +738,24 @@ describe('uiSortable', function() { inject(function($compile, $rootScope) { var elementTop, elementBottom, wrapperTop, wrapperBottom, + wrapperTopScope, wrapperBottomScope, itemsTop, itemsBottom, updateCallbackExpectations; - wrapperTop = $compile('
')($rootScope); - wrapperBottom = $compile('
')($rootScope); + wrapperTopScope = $rootScope.$new(); + wrapperBottomScope = $rootScope.$new(); + wrapperTop = $compile('
')(wrapperTopScope); + wrapperBottom = $compile('
')(wrapperBottomScope); host.append(wrapperTop).append(wrapperBottom).append('
'); $rootScope.$apply(function() { - wrapperTop.scope().itemsTop = itemsTop = ['Top One', 'Top Two', 'Top Three']; - wrapperBottom.scope().itemsBottom = itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three']; + wrapperTopScope.itemsTop = itemsTop = ['Top One', 'Top Two', 'Top Three']; + wrapperBottomScope.itemsBottom = 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) { + if (ui.item.sortable.model && + (typeof ui.item.sortable.model === 'string') && + ui.item.sortable.model.indexOf('Two') >= 0) { ui.item.sortable.cancel(); } updateCallbackExpectations(ui.item.sortable); @@ -824,9 +836,9 @@ describe('uiSortable', function() { }, 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) { + if (ui.item.sortable.model && + (typeof ui.item.sortable.model === 'string') && + ui.item.sortable.model.indexOf('Two') >= 0) { ui.item.sortable.cancel(); } } diff --git a/test/sortable.e2e.nested.spec.js b/test/sortable.e2e.nested.spec.js index ba10be9..874ba8c 100644 --- a/test/sortable.e2e.nested.spec.js +++ b/test/sortable.e2e.nested.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper')); diff --git a/test/sortable.e2e.spec.js b/test/sortable.e2e.spec.js index 987a3e7..1be9306 100644 --- a/test/sortable.e2e.spec.js +++ b/test/sortable.e2e.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper')); @@ -160,6 +166,35 @@ describe('uiSortable', function() { }); }); + it('should work when "placeholder" option equals the class of items [data-ng-repeat]', function() { + inject(function($compile, $rootScope) { + var element; + element = $compile('')($rootScope); + $rootScope.$apply(function() { + $rootScope.opts = { + placeholder: 'sortable-item' + }; + $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', 'Three', 'Two']); + expect($rootScope.items).toEqual(listContent(element)); + + li = element.find(':eq(1)'); + dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['Three', 'One', 'Two']); + expect($rootScope.items).toEqual(listContent(element)); + + $(element).remove(); + }); + }); + it('should continue to work after a drag is reverted', function() { inject(function($compile, $rootScope) { var element; diff --git a/test/sortable.spec.js b/test/sortable.spec.js index ea99908..fd664bd 100644 --- a/test/sortable.spec.js +++ b/test/sortable.spec.js @@ -2,6 +2,12 @@ describe('uiSortable', function() { + beforeEach(module(function($compileProvider) { + if (typeof $compileProvider.debugInfoEnabled === 'function') { + $compileProvider.debugInfoEnabled(false); + } + })); + // Ensure the sortable angular module is loaded beforeEach(module('ui.sortable')); beforeEach(module('ui.sortable.testHelper'));