diff --git a/addon/-private/system/model/internal-model.js b/addon/-private/system/model/internal-model.js index 73a1f0a5e90..670ca2df443 100644 --- a/addon/-private/system/model/internal-model.js +++ b/addon/-private/system/model/internal-model.js @@ -93,7 +93,7 @@ export default class InternalModel { this.modelName = modelName; this.clientId = clientId; - this._recordData = store._createRecordData(modelName, id, clientId, this); + this.__recordData = null; // this ensure ordered set can quickly identify this as unique this[Ember.GUID_KEY] = InternalModelReferenceId++ + 'internal-model'; @@ -144,6 +144,17 @@ export default class InternalModel { return this._recordReference; } + get _recordData() { + if (this.__recordData === null) { + this._recordData = this.store._createRecordData(this.modelName, this.id, this.clientId, this); + } + return this.__recordData; + } + + set _recordData(newValue) { + this.__recordData = newValue; + } + get _recordArrays() { if (this.__recordArrays === null) { this.__recordArrays = new OrderedSet(); @@ -764,6 +775,10 @@ export default class InternalModel { hasChangedAttributes() { heimdall.increment(hasChangedAttributes); + if (this.isLoading()) { + // no need to instantiate _recordData in this case + return false; + } return this._recordData.hasChangedAttributes(); } @@ -776,6 +791,10 @@ export default class InternalModel { */ changedAttributes() { heimdall.increment(changedAttributes); + if (this.isLoading()) { + // no need to calculate changed attributes when calling `findRecord` + return {}; + } return this._recordData.changedAttributes(); } diff --git a/tests/integration/records/record-data-test.js b/tests/integration/records/record-data-test.js index e8bb3b29ee6..780db925daa 100644 --- a/tests/integration/records/record-data-test.js +++ b/tests/integration/records/record-data-test.js @@ -5,6 +5,7 @@ import { run } from '@ember/runloop'; import { attr, belongsTo, hasMany } from '@ember-decorators/data'; import { assign } from '@ember/polyfills'; import { RecordData, recordDataFor } from 'ember-data/-private'; +import { resolve } from 'rsvp'; class Person extends Model { @hasMany('pet', { inverse: null, async: false }) @@ -216,4 +217,62 @@ module('RecordData Compatibility', function(hooks) { assert.ok(false, 'expected `unloadRecord()` not to throw'); } }); + + test(`store.findRecord does not eagerly instantiate record data`, async function(assert) { + let recordDataInstances = 0; + class TestRecordData extends CustomRecordData { + constructor() { + super(...arguments); + ++recordDataInstances; + } + } + + store.createRecordDataFor = function(modelName, id, lid, storeWrapper) { + return new TestRecordData(modelName, id, lid, storeWrapper); + }; + this.owner.register( + 'adapter:pet', + class TestAdapter { + static create() { + return new TestAdapter(...arguments); + } + + findRecord() { + assert.equal( + recordDataInstances, + 0, + 'no instance created from findRecord before adapter promise resolves' + ); + + return resolve({ + data: { + id: '1', + type: 'pet', + attributes: { + name: 'Loki', + }, + }, + }); + } + } + ); + this.owner.register( + 'serializer:pet', + class TestSerializer { + static create() { + return new TestSerializer(...arguments); + } + + normalizeResponse(store, modelClass, payload) { + return payload; + } + } + ); + + assert.equal(recordDataInstances, 0, 'initially no instances'); + + await store.findRecord('pet', '1'); + + assert.equal(recordDataInstances, 1, 'record data created after promise fulfills'); + }); });