From 7189bf0c0c796c6d7cd1fbcb40c81e146623cf38 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Thu, 28 Jun 2018 11:09:30 -0400 Subject: [PATCH] [BUGFIX] Setting ArrayProxy#content in willDestroy resets length. Prior to this change, `ArrayProxy.prototype.length` was marked as dirty (and therefore recomputed) when `content` was set due to `arrangedContent` being an alias of `content`, and `ArrayProxy` implementing `PROPERTY_DID_CHANGE` to trap `arrangedContent` changes. Unfortunately, `notifyPropertyChange` avoids notifying _some_ things when the object is destroying. This results in the preexisting `PROPERTY_DID_CHANGE` trap for `arrangedContent` is no longer called, and therefore `length` is never marked as dirty. This fixes the condition, by implementing a `content` trap in `PROPERTY_DID_CHANGE` that marks length as dirty. The next time that `length` is accessed it will do the normal work to recalculate. --- .../ember-runtime/lib/system/array_proxy.js | 14 +++++++++----- .../tests/system/array_proxy/length_test.js | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/ember-runtime/lib/system/array_proxy.js b/packages/ember-runtime/lib/system/array_proxy.js index 67adb343476..16e8c4b9da6 100644 --- a/packages/ember-runtime/lib/system/array_proxy.js +++ b/packages/ember-runtime/lib/system/array_proxy.js @@ -210,9 +210,7 @@ export default class ArrayProxy extends EmberObject { if (content) { replace(content, value, removedCount, added); - this._lengthDirty = true; - this._objectsDirtyIndex = 0; - this._objects = null; + this._invalidate(); } } @@ -225,11 +223,12 @@ export default class ArrayProxy extends EmberObject { this._removeArrangedContentArrayObsever(); this.arrayContentWillChange(0, oldLength, newLength); - this._objectsDirtyIndex = 0; - this._lengthDirty = true; + this._invalidate(); this.arrayContentDidChange(0, oldLength, newLength); this._addArrangedContentArrayObsever(); + } else if (key === 'content') { + this._invalidate(); } } @@ -277,6 +276,11 @@ export default class ArrayProxy extends EmberObject { this.arrayContentDidChange(idx, removedCnt, addedCnt); } + + _invalidate() { + this._objectsDirtyIndex = 0; + this._lengthDirty = true; + } } ArrayProxy.reopen(MutableArray, { diff --git a/packages/ember-runtime/tests/system/array_proxy/length_test.js b/packages/ember-runtime/tests/system/array_proxy/length_test.js index 00c4b1b0368..2ab0d106bfa 100644 --- a/packages/ember-runtime/tests/system/array_proxy/length_test.js +++ b/packages/ember-runtime/tests/system/array_proxy/length_test.js @@ -78,6 +78,24 @@ moduleFor( assert.deepEqual(obj.content, null, 'content was updated'); } + '@test accessing length after content set to null in willDestroy'(assert) { + let obj = ArrayProxy.extend({ + willDestroy() { + this.set('content', null); + this._super(...arguments); + }, + }).create({ + content: ['foo', 'bar'], + }); + + assert.equal(obj.length, 2, 'precond'); + + this.runTask(() => obj.destroy()); + + assert.equal(obj.length, 0, 'length is 0 without content'); + assert.deepEqual(obj.content, null, 'content was updated'); + } + '@test setting length to 0'(assert) { let obj = ArrayProxy.create({ content: ['foo', 'bar'] });