Skip to content
This repository was archived by the owner on Sep 8, 2020. It is now read-only.

Commit 36badd1

Browse files
committed
Merge branch 'tepez-extra_elems-rebase' into v0.14.x-dev
feat(sortable): allow extra element before/after ng-repeat
2 parents 704cc55 + 7d9a831 commit 36badd1

9 files changed

+949
-312
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ Apply the directive to your form elements:
4343
**Developing Notes:**
4444

4545
* `ng-model` is required, so that the directive knows which model to update.
46-
* `ui-sortable` element should only contain one `ng-repeat` and not any other elements (above or below).
47-
Otherwise the index matching of the generated DOM elements and the `ng-model`'s items will break.
48-
**In other words: The items of `ng-model` must match the indexes of the generated DOM elements.**
46+
* `ui-sortable` element should contain only one `ng-repeat`, but other non-repeater elements above or below may still exist.
47+
Otherwise the index matching of the `ng-model`'s items and the DOM elements generated by the `ng-repeat` will break.
48+
**In other words: The items of `ng-model` must match the indexes of the DOM elements generated by the `ng-repeat`.**
4949
* `ui-sortable` lists containing many 'types' of items can be implemented by using dynamic template loading [with ng-include](http://stackoverflow.com/questions/14607879/angularjs-load-dynamic-template-html-within-directive/14621927#14621927) or a [loader directive](https://github.com/thgreasi/tg-dynamic-directive), to determine how each model item should be rendered. Also take a look at the [Tree with dynamic template](http://codepen.io/thgreasi/pen/uyHFC) example.
5050

5151
### Options

src/sortable.js

+76-28
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,48 @@ angular.module('ui.sortable', [])
3737
return null;
3838
}
3939

40+
function getPlaceholderElement (element) {
41+
var placeholder = element.sortable('option','placeholder');
42+
43+
// placeholder.element will be a function if the placeholder, has
44+
// been created (placeholder will be an object). If it hasn't
45+
// been created, either placeholder will be false if no
46+
// placeholder class was given or placeholder.element will be
47+
// undefined if a class was given (placeholder will be a string)
48+
if (placeholder && placeholder.element && typeof placeholder.element === 'function') {
49+
var result = placeholder.element();
50+
// workaround for jquery ui 1.9.x,
51+
// not returning jquery collection
52+
result = angular.element(result);
53+
return result;
54+
}
55+
return null;
56+
}
57+
58+
function getPlaceholderExcludesludes (element, placeholder) {
59+
// exact match with the placeholder's class attribute to handle
60+
// the case that multiple connected sortables exist and
61+
// the placehoilder option equals the class of sortable items
62+
var excludes = element.find('[class="' + placeholder.attr('class') + '"]:not([ng-repeat], [data-ng-repeat])');
63+
return excludes;
64+
}
65+
4066
function hasSortingHelper (element, ui) {
4167
var helperOption = element.sortable('option','helper');
4268
return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed());
4369
}
4470

71+
function getSortingHelper (element, ui, savedNodes) {
72+
var result = null;
73+
if (hasSortingHelper(element, ui) &&
74+
element.sortable( 'option', 'appendTo' ) === 'parent') {
75+
// The .ui-sortable-helper element (that's the default class name)
76+
// is placed last.
77+
result = savedNodes.last();
78+
}
79+
return result;
80+
}
81+
4582
// thanks jquery-ui
4683
function isFloating (item) {
4784
return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display'));
@@ -63,7 +100,20 @@ angular.module('ui.sortable', [])
63100
ui.item.sortable._destroy();
64101
}
65102

66-
var opts = {};
103+
// return the index of ui.item among the items
104+
// we can't just do ui.item.index() because there it might have siblings
105+
// which are not items
106+
function getItemIndex(ui) {
107+
return ui.item.parent().find('> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]')
108+
.index(ui.item);
109+
}
110+
111+
var opts = {
112+
// the default for jquery-ui sortable is "> *", we need to restrict this to
113+
// ng-repeat items
114+
// if the user uses
115+
items: '> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]'
116+
};
67117

68118
// directive specific options
69119
var directiveOpts = {
@@ -114,9 +164,10 @@ angular.module('ui.sortable', [])
114164
}
115165

116166
// Save the starting position of dragged item
167+
var index = getItemIndex(ui);
117168
ui.item.sortable = {
118-
model: ngModel.$modelValue[ui.item.index()],
119-
index: ui.item.index(),
169+
model: ngModel.$modelValue[index],
170+
index: index,
120171
source: ui.item.parent(),
121172
sourceModel: ngModel.$modelValue,
122173
cancel: function () {
@@ -147,24 +198,9 @@ angular.module('ui.sortable', [])
147198

148199
// If this list has a placeholder (the connected lists won't),
149200
// don't inlcude it in saved nodes.
150-
var placeholder = element.sortable('option','placeholder');
151-
152-
// placeholder.element will be a function if the placeholder, has
153-
// been created (placeholder will be an object). If it hasn't
154-
// been created, either placeholder will be false if no
155-
// placeholder class was given or placeholder.element will be
156-
// undefined if a class was given (placeholder will be a string)
157-
if (placeholder && placeholder.element && typeof placeholder.element === 'function') {
158-
var phElement = placeholder.element();
159-
// workaround for jquery ui 1.9.x,
160-
// not returning jquery collection
161-
phElement = angular.element(phElement);
162-
163-
// exact match with the placeholder's class attribute to handle
164-
// the case that multiple connected sortables exist and
165-
// the placehoilder option equals the class of sortable items
166-
var excludes = element.find('[class="' + phElement.attr('class') + '"]:not([ng-repeat], [data-ng-repeat])');
167-
201+
var placeholder = getPlaceholderElement(element);
202+
if (placeholder && placeholder.length) {
203+
var excludes = getPlaceholderExcludesludes(element, placeholder);
168204
savedNodes = savedNodes.not(excludes);
169205
}
170206

@@ -184,7 +220,7 @@ angular.module('ui.sortable', [])
184220
// update that happens when moving between lists because then
185221
// the value will be overwritten with the old value
186222
if(!ui.item.sortable.received) {
187-
ui.item.sortable.dropindex = ui.item.index();
223+
ui.item.sortable.dropindex = getItemIndex(ui);
188224
var droptarget = ui.item.parent();
189225
ui.item.sortable.droptarget = droptarget;
190226

@@ -203,11 +239,11 @@ angular.module('ui.sortable', [])
203239
// the start and stop of repeat sections and sortable doesn't
204240
// respect their order (even if we cancel, the order of the
205241
// comments are still messed up).
206-
if (hasSortingHelper(element, ui) && !ui.item.sortable.received &&
207-
element.sortable( 'option', 'appendTo' ) === 'parent') {
208-
// restore all the savedNodes except .ui-sortable-helper element
209-
// (which is placed last). That way it will be garbage collected.
210-
savedNodes = savedNodes.not(savedNodes.last());
242+
var sortingHelper = !ui.item.sortable.received && getSortingHelper(element, ui, savedNodes);
243+
if (sortingHelper && sortingHelper.length) {
244+
// Restore all the savedNodes except from the sorting helper element.
245+
// That way it will be garbage collected.
246+
savedNodes = savedNodes.not(sortingHelper);
211247
}
212248
savedNodes.appendTo(element);
213249

@@ -248,7 +284,15 @@ angular.module('ui.sortable', [])
248284
// if the item was not moved, then restore the elements
249285
// so that the ngRepeat's comment are correct.
250286
if ((!('dropindex' in ui.item.sortable) || ui.item.sortable.isCanceled()) &&
251-
!hasSortingHelper(element, ui)) {
287+
!angular.equals(element.contents(), savedNodes)) {
288+
289+
var sortingHelper = getSortingHelper(element, ui, savedNodes);
290+
if (sortingHelper && sortingHelper.length) {
291+
// Restore all the savedNodes except from the sorting helper element.
292+
// That way it will be garbage collected.
293+
savedNodes = savedNodes.not(sortingHelper);
294+
}
295+
savedNodes.appendTo(element);
252296
savedNodes.appendTo(element);
253297
}
254298
}
@@ -325,6 +369,10 @@ angular.module('ui.sortable', [])
325369
value = wrappers[key](value);
326370
}
327371

372+
if (key === 'items' && !value) {
373+
value = '> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]';
374+
}
375+
328376
opts[key] = value;
329377
element.sortable('option', key, value);
330378
});

0 commit comments

Comments
 (0)