diff --git a/packages/ember-data/lib/system/many-array.js b/packages/ember-data/lib/system/many-array.js index 140a0a46ded..1ef9915c8ad 100644 --- a/packages/ember-data/lib/system/many-array.js +++ b/packages/ember-data/lib/system/many-array.js @@ -2,6 +2,7 @@ @module ember-data */ import { PromiseArray } from "ember-data/system/promise-proxies"; +import FilteredSubset from "ember-data/system/record-arrays/filtered-subset"; var get = Ember.get; var set = Ember.set; @@ -277,5 +278,37 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, { this.pushObject(record); return record; + }, + + /** + Get a filtered subset of the underlying `ManyArray`. + The subset updates when a record would match or mismatch the + specified filter parameters. + + Example + + ```javascript + var post = store.peekRecord('post', 1) + // All the comments that are deleted locally but not yet saved to the server. + var deletedComments = post.get('comments').filterBy('isDeleted'); + ``` + + @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 + }); } }); diff --git a/packages/ember-data/lib/system/record-arrays/filtered-subset.js b/packages/ember-data/lib/system/record-arrays/filtered-subset.js new file mode 100644 index 00000000000..4a11c95e1ca --- /dev/null +++ b/packages/ember-data/lib/system/record-arrays/filtered-subset.js @@ -0,0 +1,15 @@ +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); + })); + } +}); + +export default FilteredSubset; 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 dabf20a90c7..5c5c0bb614d 100644 --- a/packages/ember-data/lib/system/record-arrays/record-array.js +++ b/packages/ember-data/lib/system/record-arrays/record-array.js @@ -4,25 +4,11 @@ import { PromiseArray } from "ember-data/system/promise-proxies"; import SnapshotRecordArray from "ember-data/system/snapshot-record-array"; +import FilteredSubset from "ember-data/system/record-arrays/filtered-subset"; 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 diff --git a/packages/ember-data/tests/integration/relationships/has-many-test.js b/packages/ember-data/tests/integration/relationships/has-many-test.js index 7e02af05c75..9a1cb3dca67 100644 --- a/packages/ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/ember-data/tests/integration/relationships/has-many-test.js @@ -2340,3 +2340,75 @@ test("metadata should be reset between requests", function() { }); }); }); + + +test("filterBy - returns a filtered subset", function () { + var chapter, page; + run(function() { + env.store.push({ + data: { + type: 'chapter', + id: '1', + relationships: { + pages: { + data: [ + { type: 'page', id: '2' }, + { type: 'page', id: '3' } + ] + } + } + }, + included: [{ + type: 'page', + id: '2' + }, { + type: 'page', + id: '3' + }] + }); + chapter = env.store.peekRecord('chapter', 1); + page = env.store.peekRecord('page', 2); + }); + run(function() { + page.deleteRecord(); + }); + run(function() { + equal(chapter.get('pages').filterBy('isDeleted').get('length'), 1, "Can filter by deleted records"); + }); +}); + + +test("filterBy - returns a filtered subset", function () { + var chapter; + run(function() { + env.store.push({ + data: { + type: 'chapter', + id: '1', + relationships: { + pages: { + data: [ + { type: 'page', id: '2' }, + { type: 'page', id: '3' } + ] + } + } + }, + included: [{ + type: 'page', + id: '2' + }, { + type: 'page', + id: '3' + }] + }); + chapter = env.store.peekRecord('chapter', 1); + }); + run(function() { + env.store.peekRecord('page', 2).deleteRecord(); + var deletedChapters = chapter.get('pages').filterBy('isDeleted'); + equal(deletedChapters.get('length'), 1); + env.store.peekRecord('page', 3).deleteRecord(); + equal(deletedChapters.get('length'), 2); + }); +});