UNPKG

ember-m3

Version:

Alternative to @ember-data/model in which attributes and relationships are derived from API Payloads

147 lines (133 loc) 4.59 kB
import MutableArray from '@ember/array/mutable'; import EmberObject, { get } from '@ember/object'; import { resolveValue } from './resolve-attribute-util'; import { isResolvedValue } from './utils/resolve'; import { associateRecordWithRecordArray } from './record-array'; import { recordDataFor } from './-private'; import { deprecate } from '@ember/debug'; import { CUSTOM_MODEL_CLASS } from 'ember-m3/-infra/features'; import MegamorphicModel from './model'; import { recordDataToRecordMap } from './utils/caches'; import { recordIdentifierFor } from '@ember-data/store'; /** * M3TrackedArray * * @class M3TrackedArray * @extends {Ember.ArrayProxy} */ export default class M3TrackedArray extends EmberObject.extend(MutableArray) { init() { super.init(...arguments); this._key = get(this, 'key'); this._modelName = get(this, 'modelName'); this._store = get(this, 'store'); this._schema = get(this, 'schema'); this._record = get(this, 'model'); } get value() { deprecate('Accessing value on an M3TrackedArray was private and is deprecated.', false, { id: 'm3.tracked-array.value', until: '1.0', }); return this._value; } replace(idx, removeAmt, newItems) { this.replaceContent(idx, removeAmt, newItems); } objectAt(idx) { return this.content[idx]; } replaceContent(idx, removeAmt, newItems) { // Update childRecordDatas array // mapping to array of nested models recordDataFor(this._record)._resizeChildRecordData(this._key, idx, removeAmt, newItems.length); newItems = newItems.map((item, index) => { if (isResolvedValue(item)) { associateRecordWithRecordArray(item, this); let parentRecordData = recordDataFor(this._record); let childRecordData; if (item instanceof MegamorphicModel) { childRecordData = recordDataFor(item); } else { if (CUSTOM_MODEL_CLASS) { let identifier = recordIdentifierFor(item); childRecordData = parentRecordData.storeWrapper.recordDataFor( identifier.type, identifier.id, identifier.lid ); } else { childRecordData = recordDataFor(item); } } // TODO: clean up this ridiculous hack // adding a resolved value to a tracked array requires the child model // data stitching to be maintained parentRecordData._setChildRecordData(this._key, index + idx, childRecordData); return item; } return resolveValue( this._key, item, this._modelName, this._store, this._schema, this._record, index + idx ); }); // Update content this.arrayContentWillChange(idx, removeAmt, newItems.length); this.content.replace(idx, removeAmt, newItems); this.arrayContentDidChange(idx, removeAmt, newItems.length); // Set attribute in recordData and update model state and changedAttributes // object this._record._setAttribute(this._key, this.content, true); } get length() { return this.content && this.content.length !== undefined ? this.content.length : 0; } _removeInternalModels(internalModels) { if (CUSTOM_MODEL_CLASS) { throw new Error('Should not be calling _removeInternalModels when CUSTOM_MODEL_CLASS is on'); } else { for (let i = this.content.length; i >= 0; --i) { let item = this.content.objectAt(i); if (isResolvedValue(item)) { for (let j = 0; j < internalModels.length; ++j) { let internalModel = internalModels[j]; if (internalModel === item._internalModel) { this.arrayContentWillChange(i, 1, 0); this.content.removeAt(i); this.arrayContentDidChange(i, 1, 0); break; } } } } } } _removeObject(record) { let index = this.content.indexOf(record); if (index > -1) { this.arrayContentWillChange(index, 1, 0); this.content.removeObject(record); this.arrayContentDidChange(index, 1, 0); } } _removeRecordData(recordData) { let recordToMatch = recordDataToRecordMap.get(recordData); if (!recordToMatch) { return; } for (let i = this.content.length; i >= 0; --i) { let item = this.content.objectAt(i); if (recordToMatch === item) { this.arrayContentWillChange(i, 1, 0); this.content.removeAt(i); this.arrayContentDidChange(i, 1, 0); break; } } } }