Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX beta] Ember.computed.sort Observers #5150

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions packages/ember-runtime/lib/computed/reduce_computed_macros.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import {
forEach
} from 'ember-metal/enumerable_utils';
import run from 'ember-metal/run_loop';
import { addObserver } from 'ember-metal/observer';
import { arrayComputed } from 'ember-runtime/computed/array_computed';
import { reduceComputed } from 'ember-runtime/computed/reduce_computed';
import ObjectProxy from 'ember-runtime/system/object_proxy';
import SubArray from 'ember-runtime/system/subarray';
import keys from 'ember-metal/keys';
import compare from 'ember-runtime/compare';
import { addObserver, removeObserver } from "ember-metal/observer";
import { arrayComputed } from "ember-runtime/computed/array_computed";
import { reduceComputed } from "ember-runtime/computed/reduce_computed";
import ObjectProxy from "ember-runtime/system/object_proxy";
import SubArray from "ember-runtime/system/subarray";
import keys from "ember-runtime/keys";
import compare from "ember-runtime/compare";

var a_slice = [].slice;

Expand Down Expand Up @@ -710,7 +710,7 @@ export function sort(itemsKey, sortDefinition) {
Ember.assert('Ember.computed.sort requires two arguments: an array key to sort and ' +
'either a sort properties key or sort function', arguments.length === 2);

var initFn, sortPropertiesKey;
var initFn, recomputePropFn, recomputeEachFn, sortPropertiesKey;

if (typeof sortDefinition === 'function') {
initFn = function (array, changeMeta, instanceMeta) {
Expand All @@ -721,6 +721,11 @@ export function sort(itemsKey, sortDefinition) {
sortPropertiesKey = sortDefinition;

initFn = function (array, changeMeta, instanceMeta) {

function recomputeOnce() {
changeMeta.property.recomputeOnce.call(this, changeMeta.propertyName);
}

function setupSortProperties() {
var sortPropertyDefinitions = get(this, sortPropertiesKey);
var sortProperties = instanceMeta.sortProperties = [];
Expand All @@ -746,19 +751,17 @@ export function sort(itemsKey, sortDefinition) {
changeMeta.property.itemPropertyKey(itemsKey, sortProperty);
});

sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce);
}

function updateSortPropertiesOnce() {
run.once(this, updateSortProperties, changeMeta.propertyName);
if (recomputeEachFn) {
removeObserver(sortPropertyDefinitions, '@each', this, recomputeEachFn);
}
addObserver(sortPropertyDefinitions, '@each', this, recomputeEachFn = recomputeOnce);
}

function updateSortProperties(propertyName) {
setupSortProperties.call(this);
changeMeta.property.recomputeOnce.call(this, propertyName);
if (recomputePropFn) {
removeObserver(this, sortPropertiesKey, recomputePropFn);
}
addObserver(this, sortPropertiesKey, recomputePropFn = recomputeOnce);

addObserver(this, sortPropertiesKey, updateSortPropertiesOnce);
setupSortProperties.call(this);

instanceMeta.order = function (itemA, itemB) {
Expand Down
122 changes: 122 additions & 0 deletions packages/ember-runtime/tests/computed/reduce_computed_macros_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,128 @@ test("changing item properties not specified via @each does not trigger a resort
deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], "updating an unspecified property on an item does not resort it");
});

QUnit.module('computedSort - sort observers', {
setup: function() {
run(function() {
obj = EmberObject.createWithMixins({
items: Ember.A([{
fname: "Jaime", lname: "Lannister", age: 34
}, {
fname: "Cersei", lname: "Lannister", age: 34
}, {
fname: "Robb", lname: "Stark", age: 16
}, {
fname: "Bran", lname: "Stark", age: 8
}]),
itemSorting: Ember.A(['lname', 'fname']),
sortedItems: computedSort('items', 'itemSorting'),

i: 0,
init: function() {
this._super();

var self = this;
var cp = Ember.meta(this).descs['sortedItems'];
var ro = cp.recomputeOnce;
cp.recomputeOnce = function(propertyName) {
self.incrementProperty('i', 1);
ro.call(this, propertyName);
};
}
});
});
},
teardown: function() {
run(function() {
obj.destroy();
});
}
});

test("updating the sort properties definitions array content triggers recomputeOnce only one time", function() {
run(function() {
get(obj, 'sortedItems'); // consume computed
});

run(function() {
set(obj, 'i', 0);
toggleSort(get(obj, 'itemSorting'), 0);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the first item in sort property definitions is toggled to desc');

run(function() {
set(obj, 'i', 0);
toggleSort(get(obj, 'itemSorting'), 1);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the second item in sort property definitions is toggled to desc');

run(function() {
set(obj, 'i', 0);
toggleSort(get(obj, 'itemSorting'), 0);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the first item in sort property definitions is toggled back to asc');

run(function() {
set(obj, 'i', 0);
toggleSort(get(obj, 'itemSorting'), 1);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the second item in sort property definitions is toggled back to asc');
});

test("replacing the sort property definitions array itself triggers recomputeOnce only one time", function() {
var spd;

run(function() {
get(obj, 'sortedItems'); // consume computed
});

run(function() {
set(obj, 'i', 0);
toggleSort(spd = Ember.A(get(obj, 'itemSorting').slice()), 0);
set(obj, 'itemSorting', spd);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the first item in sort property definitions is toggled to desc');

run(function() {
set(obj, 'i', 0);
toggleSort(spd = Ember.A(get(obj, 'itemSorting').slice()), 1);
set(obj, 'itemSorting', spd);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the second item in sort property definitions is toggled to desc');

run(function() {
set(obj, 'i', 0);
toggleSort(spd = Ember.A(get(obj, 'itemSorting').slice()), 0);
set(obj, 'itemSorting', spd);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the first item in sort property definitions is toggled back to asc');

run(function() {
set(obj, 'i', 0);
toggleSort(spd = Ember.A(get(obj, 'itemSorting').slice()), 1);
set(obj, 'itemSorting', spd);
});

equal(get(obj, 'i'), 1, 'recomputeOnce called once when sort direction of the second item in sort property definitions is toggled back to asc');
});

function toggleSort(spd, idx) {
var def = spd.objectAt(idx);
if (def.indexOf(':desc') !== -1) {
def = def.slice(0, -5);
} else {
def += ':desc';
}
spd.replace(0, 1, [def]);
}

QUnit.module('computedMax', {
setup: function() {
run(function() {
Expand Down