Deprecation Guide for Deprecation of @ember/array Foundation APIs
The foundational APIs of @ember/array
, including the A()
function and the core mixins (EmberArray
, MutableArray
, NativeArray
), are deprecated. These were used to create and extend arrays with Ember's observability. The modern approach is to use native JavaScript arrays, and TrackedArray
from tracked-built-ins
when reactivity is needed.
A()
Function and Core Mixins
The A()
function would wrap a native array, making it an EmberArray
. The EmberArray
and MutableArray
mixins could be used to build custom array-like classes.
Before
import { A } from '@ember/array';
import EmberObject from '@ember/object';
import { MutableArray } from '@ember/array';
let emberArr = A([1, 2, 3]);
emberArr.pushObject(4);
const MyArray = EmberObject.extend(MutableArray, {
// ... implementation ...
});
After
Use native arrays for standard array operations. For arrays that need to be tracked for reactivity in components and other classes, use TrackedArray
from the tracked-built-ins
addon.
// For a standard array
let nativeArr = [1, 2, 3];
nativeArr.push(4);
// For a tracked array
import { TrackedArray } from 'tracked-built-ins';
let trackedArr = new TrackedArray([1, 2, 3]);
trackedArr.push(4); // This mutation is tracked
Utility Functions: isArray
and makeArray
These functions helped create and check for arrays.
Before
import { isArray, makeArray } from '@ember/array';
let isArr = isArray([]);
let ensuredArr = makeArray('hello');
After Use native JavaScript equivalents.
// isArray() -> Array.isArray()
let isArr = Array.isArray([]);
// makeArray() -> custom helper or ensure data is correct
function ensureArray(value) {
if (value === null || value === undefined) return [];
return Array.isArray(value) ? value : [value];
}
let ensuredArr = ensureArray('hello');
Creating Custom Arrays with Proxies
For advanced use cases where you might have created a custom class based on MutableArray
to add special behaviors to your array, the modern JavaScript equivalent is to use a Proxy
. A Proxy
object allows you to intercept and redefine fundamental operations for a target object (like an array), enabling you to create powerful custom wrappers.
Example: A Logging Array
Imagine you want to log every time an item is pushed to an array.
Before, you might have done this with MutableArray
:
import EmberObject from '@ember/object';
import { MutableArray } from '@ember/array';
const LoggingArray = EmberObject.extend(MutableArray, {
// Internal content array
_content: null,
init() {
this._super(...arguments);
this._content = this._content || [];
},
// Required primitives
objectAt(idx) { return this._content[idx]; },
get length() { return this._content.length; },
// Override replace to add logging
replace(idx, amt, objects) {
if (amt === 0) {
console.log(`Adding items: ${objects.join(', ')}`);
}
this._content.splice(idx, amt, ...objects);
this.arrayContentDidChange(idx, amt, objects.length);
}
});
let arr = LoggingArray.create({ _content: [1, 2] });
arr.pushObject(3); // Logs: "Adding items: 3"
After, you can achieve the same result more cleanly by wrapping a TrackedArray
in a Proxy
. This allows you to add custom behavior while preserving the reactivity provided by TrackedArray
.
import { TrackedArray } from 'tracked-built-ins';
function createTrackedLoggingArray(initialItems) {
// Start with a TrackedArray instance
const trackedArr = new TrackedArray(initialItems);
return new Proxy(trackedArr, {
get(target, prop, receiver) {
// Intercept the 'push' method
if (prop === 'push') {
return function(...args) {
console.log(`Adding items via push: ${args.join(', ')}`);
// Call the original push method on the TrackedArray
// This will trigger reactivity automatically.
return target.push(...args);
}
}
// Forward all other property access and method calls to the TrackedArray
const value = Reflect.get(target, prop, receiver);
return typeof value === 'function' ? value.bind(target) : value;
},
set(target, prop, value, receiver) {
// Intercept direct index assignment
if (!isNaN(parseInt(prop, 10))) {
console.log(`Setting index ${prop} to ${value}`);
}
// Forward the set operation to the TrackedArray to trigger reactivity
return Reflect.set(target, prop, value, receiver);
}
});
}
// In a component:
class MyComponent {
loggingArray = createTrackedLoggingArray([1, 2]);
addItem() {
this.loggingArray.push(3); // Logs and triggers an update
}
updateItem() {
this.loggingArray[0] = 'new value'; // Logs and triggers an update
}
}
This Proxy
approach is very powerful. By wrapping a TrackedArray
, you can layer in custom logic while letting it handle the complexities of reactivity. This is the recommended pattern for creating advanced, observable array-like objects in modern Ember.