diff --git a/content/ember/v6/deprecate-ember-array-foundations.md b/content/ember/v6/deprecate-ember-array-foundations.md new file mode 100644 index 00000000..daa26fe7 --- /dev/null +++ b/content/ember/v6/deprecate-ember-array-foundations.md @@ -0,0 +1,160 @@ +--- +title: Deprecation of @ember/array Foundation APIs +until: "6.0" +since: "5.8" +displayId: array.foundations +--- + +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** +```javascript +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. + +```javascript +// 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** +```javascript +import { isArray, makeArray } from '@ember/array'; +let isArr = isArray([]); +let ensuredArr = makeArray('hello'); +``` + +**After** +Use native JavaScript equivalents. + +```javascript +// 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`: + +```javascript +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`. + +```javascript +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. diff --git a/content/ember/v6/deprecate-ember-array-read-methods.md b/content/ember/v6/deprecate-ember-array-read-methods.md new file mode 100644 index 00000000..17af6836 --- /dev/null +++ b/content/ember/v6/deprecate-ember-array-read-methods.md @@ -0,0 +1,101 @@ +--- +title: Deprecation of @ember/array Read Methods +until: "6.0" +since: "5.8" +displayId: array.read-methods +--- + +All read-only methods and computed properties from `@ember/array` are deprecated. You should use native JavaScript array methods and properties instead. This guide covers the common read-only APIs and their native equivalents. + +### `firstObject` and `lastObject` + +These computed properties provided safe access to the first and last elements of an array. + +**Before** +```javascript +import { A } from '@ember/array'; +let arr = A(['a', 'b', 'c']); +let first = arr.get('firstObject'); // 'a' +let last = arr.get('lastObject'); // 'c' +``` + +**After** +Use native array bracket notation, or the `at()` method for accessing elements from the end of the array. + +```javascript +let arr = ['a', 'b', 'c']; +let first = arr[0]; +let last = arr.at(-1); +``` + +### `objectAt` and `objectsAt` + +These methods provided safe, index-based access to array elements. + +**Before** +```javascript +import { A } from '@ember/array'; +let arr = A(['a', 'b', 'c']); +let middle = arr.objectAt(1); // 'b' +let some = arr.objectsAt([0, 2]); // ['a', 'c'] +``` + +**After** +Use native array bracket notation for `objectAt`. For `objectsAt`, you can use `map`. + +```javascript +let arr = ['a', 'b', 'c']; +let middle = arr[1]; +let some = [0, 2].map(index => arr[index]); +``` + +### `mapBy`, `filterBy`, `rejectBy`, `findBy` + +These methods were shortcuts for common mapping and filtering operations on arrays of objects. + +**Before** +```javascript +import { A } from '@ember/array'; +let users = A([ + { name: 'John', isActive: true }, + { name: 'Jane', isActive: false }, +]); +let names = users.mapBy('name'); +let active = users.filterBy('isActive', true); +let john = users.findBy('name', 'John'); +``` + +**After** +Use the native `map`, `filter`, and `find` methods with arrow functions. + +```javascript +let users = [ + { name: 'John', isActive: true }, + { name: 'Jane', isActive: false }, +]; +let names = users.map(user => user.name); +let active = users.filter(user => user.isActive === true); +let john = users.find(user => user.name === 'John'); +``` + +### `uniqBy` + +`uniqBy` created a new array with unique elements based on a property. + +**Before** +```javascript +import { uniqBy } from '@ember/array'; +let users = [{ id: 1 }, { id: 2 }, { id: 1 }]; +let unique = uniqBy(users, 'id'); +``` + +**After** +Use a `Map` to efficiently create a unique list. + +```javascript +let users = [{ id: 1 }, { id: 2 }, { id: 1 }]; +let unique = Array.from( + users.reduce((map, user) => map.set(user.id, user), new Map()).values() +); +``` + diff --git a/content/ember/v6/deprecate-ember-array-write-methods.md b/content/ember/v6/deprecate-ember-array-write-methods.md new file mode 100644 index 00000000..be357d0d --- /dev/null +++ b/content/ember/v6/deprecate-ember-array-write-methods.md @@ -0,0 +1,94 @@ +--- +title: Deprecation of @ember/array Write Methods +until: "6.0" +since: "5.8" +displayId: array.write-methods +--- + +All methods from `@ember/array` that mutate or "write" to an array are deprecated. This includes observable methods like `pushObject`. + +The modern approach is to use `TrackedArray` from the [`tracked-built-ins`](https://github.com/tracked-tools/tracked-built-ins) addon. This class provides a tracked version of the native JavaScript `Array` that can be mutated directly, and these mutations will be tracked automatically. + +First, install the addon: + +```bash +ember install tracked-built-ins +``` + +### Observable Write Methods + +Methods like `pushObject`, `popObject`, `removeObject`, `insertAt`, and `removeAt` were used to modify arrays in a way that Ember's classic observability system could track. + +**Before** +```javascript +import { A } from '@ember/array'; +let arr = A([1, 2, 3]); + +arr.pushObject(4); +arr.removeAt(1, 1); // remove 1 item at index 1 +``` + +**After** +Use `TrackedArray` and mutate it directly with standard JavaScript array methods. Using `TrackedArray` provides an ergonomic API that is nearly identical to working with plain JavaScript arrays, while providing the reactivity needed for your application's UI to update automatically. + +```javascript +import { TrackedArray } from 'tracked-built-ins'; + +// In a component or class +class MyComponent { + myArray = new TrackedArray([1, 2, 3]); + + addItem() { + // pushObject -> push + this.myArray.push(4); + } + + removeItem() { + // removeAt -> splice + this.myArray.splice(1, 1); + } + + clearItems() { + // clear -> set length to 0 + this.myArray.length = 0; + } +} +``` + +This pattern applies to all mutation methods. Here is a brief mapping for the most common methods: + +| `@ember/array` Method | Native `TrackedArray` Method | +|-----------------------|------------------------------| +| `pushObject(s)` | `push` / `...` (spread) | +| `popObject()` | `pop` | +| `shiftObject()` | `shift` | +| `unshiftObject(s)` | `unshift` | +| `insertAt(idx, obj)` | `splice(idx, 0, obj)` | +| `removeAt(idx, len)` | `splice(idx, len)` | +| `clear()` | `length = 0` | +| `replace()` | `splice` | + +### Handling Uniqueness and Specific Objects + +For methods like `addObject` and `removeObject` that deal with specific object instances or uniqueness, you need a bit more logic. + +```javascript +// In your component class with `myArray = new TrackedArray([...])` + +// removeObject replacement +removeItem(item) { + const index = this.myArray.indexOf(item); + if (index > -1) { + this.myArray.splice(index, 1); + } +} + +// addObject replacement +addUniqueItem(item) { + if (!this.myArray.includes(item)) { + this.myArray.push(item); + } +} +``` + +Alternatively, if you are working with a list that must be unique, consider using a `Set` or `TrackedSet` from `tracked-built-ins`, as they handle uniqueness automatically.