From 1aeaa9124791cf0590ecfe2685ee2322e9798e0d Mon Sep 17 00:00:00 2001 From: pangratz Date: Mon, 8 Jun 2015 22:18:33 +0200 Subject: [PATCH] Add `RecordArray#filterBy` which contains a live, filtered subset This method takes the parameters `key` and an optional `value` and returns an updating list of records of the `RecordArray`, which' property value of `key` matches `value`. --- .../lib/system/record-arrays/record-array.js | 53 +++++++++++ .../tests/unit/record-array-test.js | 88 +++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/packages/ember-data/lib/system/record-arrays/record-array.js b/packages/ember-data/lib/system/record-arrays/record-array.js index 0577f2ef3f4..dabf20a90c7 100644 --- a/packages/ember-data/lib/system/record-arrays/record-array.js +++ b/packages/ember-data/lib/system/record-arrays/record-array.js @@ -8,6 +8,21 @@ import SnapshotRecordArray from "ember-data/system/snapshot-record-array"; var get = Ember.get; var set = Ember.set; +var FilteredSubset = Ember.ArrayProxy.extend({ + init: function() { + this._super(...arguments); + + var { filterByArgs, recordArray } = this.getProperties('filterByArgs', 'recordArray'); + var [key] = filterByArgs; + + var path = `recordArray.@each.${key}`; + Ember.defineProperty(this, 'content', Ember.computed(path, function() { + return this.filterBy.apply(recordArray, filterByArgs); + })); + } +}); + + /** A record array is an array that contains records of a certain type. The record array materializes records as needed when they are retrieved for the first @@ -96,6 +111,44 @@ export default Ember.ArrayProxy.extend(Ember.Evented, { return internalModel && internalModel.getRecord(); }, + /** + Get a filtered subset of the underlying `RecordArray`. + The subset updates when a record would match or mismatch the + specified filter parameters. + + Example + + ```javascript + var allToms = store.all('person').filterBy('name', 'Tom'); + + allToms.get('length'); // 0, since no toms yet in store + + var tom = store.push('person', { id: 1, name: 'Tom' }); + allToms.get('length'); // Tom is added + + tom.set('name', 'Thomas'); + allToms.get('length'); // 0, since no more records with name === 'Tom' + ``` + + @method filterBy + @param {String} key property path + @param {*} value optional + + */ + filterBy: function(key, value) { + // only pass value to the arguments if it is present; this mimics the same + // behavior for `filterBy`: http://git.io/vIurH + var filterByArgs = [key]; + if (arguments.length === 2) { + filterByArgs.push(value); + } + + return FilteredSubset.create({ + filterByArgs, + recordArray: this + }); + }, + /** Used to get the latest version of all of the records in this array from the adapter. diff --git a/packages/ember-data/tests/unit/record-array-test.js b/packages/ember-data/tests/unit/record-array-test.js index 16a4c674544..5d616de9c85 100644 --- a/packages/ember-data/tests/unit/record-array-test.js +++ b/packages/ember-data/tests/unit/record-array-test.js @@ -284,3 +284,91 @@ test("a record array should return a promise when updating", function() { }); ok(promise.then && typeof promise.then === "function", "#update returns a promise"); }); + +test('filterBy - returns a filtered subset', function() { + var store = createStore({ + person: Person + }); + + run(function() { + store.pushMany('person', [{ id: '1', name: "Tom" }, { id: '2', name: "Yehuda" }, { id: '3', name: "Alex" }]); + }); + + var all = store.all('person'); + var toms = all.filterBy('name', 'Tom'); + equal(toms.get('length'), 1); + deepEqual(toms.getEach('id'), ['1']); + + // a new record is added if filter matches + run(function() { + store.push('person', { id: '4', name: "Tom" }); + }); + equal(toms.get('length'), 2); + deepEqual(toms.getEach('id'), ['1', '4']); + + // a new record is not added if filter doesn't match + run(function() { + store.push('person', { id: '5', name: "Igor" }); + }); + equal(toms.get('length'), 2); + deepEqual(toms.getEach('id'), ['1', '4']); + + // changing the filtered value remvoves the record from the list + run(function() { + // we are using a private method here to get the record immediatly + store.recordForId('person', '1').set('name', "Thomas"); + }); + equal(toms.get('length'), 1); + deepEqual(toms.getEach('id'), ['4']); + + // change value back to original + run(function() { + store.recordForId('person', '1').set('name', "Tom"); + }); + equal(toms.get('length'), 2); + deepEqual(toms.getEach('id'), ['1', '4']); +}); + +test('filterBy - value is optional', function() { + var store = createStore({ + person: Person + }); + + run(function() { + store.pushMany('person', [{ id: '1', name: "Tom" }, { id: '2' }]); + }); + + var all = store.all('person'); + var allWithNames = all.filterBy('name'); + equal(allWithNames.get('length'), 1); + deepEqual(allWithNames.getEach('id'), ['1']); + + // a new record is added if filter matches + run(function() { + store.push('person', { id: '3', name: "Igor" }); + }); + equal(allWithNames.get('length'), 2); + deepEqual(allWithNames.getEach('id'), ['1', '3']); + + // a new record is not added if filter doesn't match + run(function() { + store.push('person', { id: '4' }); + }); + equal(allWithNames.get('length'), 2); + deepEqual(allWithNames.getEach('id'), ['1', '3']); + + // changing the filtered value remvoves the record from the list + run(function() { + // we are using a private method here to get the record immediatly + store.recordForId('person', '1').set('name', null); + }); + equal(allWithNames.get('length'), 1); + deepEqual(allWithNames.getEach('id'), ['3']); + + // change value back to original + run(function() { + store.recordForId('person', '1').set('name', "Tom"); + }); + equal(allWithNames.get('length'), 2); + deepEqual(allWithNames.getEach('id'), ['1', '3']); +});