Skip to content

Commit

Permalink
[BUGFIX beta] Fix a regression in Ember.computed.sort
Browse files Browse the repository at this point in the history
Resolves a regression in `Ember.computed.sort` that has existed since
2.0.0. The regression occurred when there were multiple instances of a
class using `Ember.computed.sort` and caused the sorted value to stop
updating.

Closes #12212.
Closes #12215.
Closes #12221.
Closes #12516.

(cherry picked from commit f05bd2d)
  • Loading branch information
mmun authored and rwjblue committed Feb 21, 2016
1 parent 3b9704c commit f61014d
Showing 1 changed file with 58 additions and 36 deletions.
94 changes: 58 additions & 36 deletions packages/ember-runtime/lib/computed/reduce_computed_macros.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { isArray } from 'ember-runtime/utils';
import { A as emberA } from 'ember-runtime/system/native_array';
import isNone from 'ember-metal/is_none';
import getProperties from 'ember-metal/get_properties';
import WeakMap from 'ember-metal/weak_map';

function reduceMacro(dependentKey, callback, initialValue) {
return computed(`${dependentKey}.[]`, function() {
Expand Down Expand Up @@ -608,53 +609,74 @@ function customSort(itemsKey, comparator) {
// This one needs to dynamically set up and tear down observers on the itemsKey
// depending on the sortProperties
function propertySort(itemsKey, sortPropertiesKey) {
var cp = new ComputedProperty(function(key) {
function didChange() {
this.notifyPropertyChange(key);
}
let cp = new ComputedProperty(function(key) {
let itemsKeyIsAtThis = (itemsKey === '@this');
let sortProperties = get(this, sortPropertiesKey);

assert(
`The sort definition for '${key}' on ${this} must be a function or an array of strings`,
isArray(sortProperties) && sortProperties.every(s => typeof s === 'string')
);

var items = itemsKey === '@this' ? this : get(this, itemsKey);
var sortProperties = get(this, sortPropertiesKey);
let normalizedSortProperties = normalizeSortProperties(sortProperties);

if (items === null || typeof items !== 'object') { return emberA(); }
// Add/remove property observers as required.
let activeObserversMap = cp._activeObserverMap || (cp._activeObserverMap = new WeakMap());
let activeObservers = activeObserversMap.get(this);

// TODO: Ideally we'd only do this if things have changed
if (cp._sortPropObservers) {
cp._sortPropObservers.forEach(args => removeObserver.apply(null, args));
if (activeObservers) {
activeObservers.forEach(args => {
removeObserver.apply(null, args);
});
}

cp._sortPropObservers = [];
function sortPropertyDidChange() {
this.notifyPropertyChange(key);
}

if (!isArray(sortProperties)) { return items; }
activeObservers = normalizedSortProperties.map(([prop]) => {
let path = itemsKeyIsAtThis ? `@each.${prop}` : `${itemsKey}.@each.${prop}`;
let args = [this, path, sortPropertyDidChange];
addObserver.apply(null, args);
return args;
});

// Normalize properties
var normalizedSort = sortProperties.map(p => {
let [prop, direction] = p.split(':');
direction = direction || 'asc';
activeObserversMap.set(this, activeObservers);

return [prop, direction];
});
// Sort and return the array.
let items = itemsKeyIsAtThis ? this : get(this, itemsKey);

// TODO: Ideally we'd only do this if things have changed
// Add observers
normalizedSort.forEach(prop => {
var args = [this, `${itemsKey}.@each.${prop[0]}`, didChange];
cp._sortPropObservers.push(args);
addObserver.apply(null, args);
});
if (isArray(items)) {
return sortByNormalizedSortProperties(items, normalizedSortProperties);
} else {
return emberA();
}
});

return emberA(items.slice().sort((itemA, itemB) => {
for (var i = 0; i < normalizedSort.length; ++i) {
var [prop, direction] = normalizedSort[i];
var result = compare(get(itemA, prop), get(itemB, prop));
if (result !== 0) {
return (direction === 'desc') ? (-1 * result) : result;
}
}
cp._activeObserverMap = undefined;

return 0;
}));
return cp.property(`${sortPropertiesKey}.[]`).readOnly();
}

function normalizeSortProperties(sortProperties) {
return sortProperties.map(p => {
let [prop, direction] = p.split(':');
direction = direction || 'asc';

return [prop, direction];
});
}

function sortByNormalizedSortProperties(items, normalizedSortProperties) {
return emberA(items.slice().sort((itemA, itemB) => {
for (let i = 0; i < normalizedSortProperties.length; i++) {
let [prop, direction] = normalizedSortProperties[i];
let result = compare(get(itemA, prop), get(itemB, prop));
if (result !== 0) {
return (direction === 'desc') ? (-1 * result) : result;
}
}

return cp.property(`${itemsKey}.[]`, `${sortPropertiesKey}.[]`).readOnly();
return 0;
}));
}

0 comments on commit f61014d

Please sign in to comment.