Deprecations Added in Ember 5.x
What follows is a list of deprecations introduced to Ember during the 5.x cycle.
For more information on deprecations in Ember, see the main deprecations page.
Deprecations Added in 5.3.0
Implicit Route Model
Previously, if no Route#model
hook was specified and a _id
parameter was present, Ember would attempt to figure out how to load that model for you. Specify your own model hook to load from the store, if desired.
An optional feature, called no-implicit-route-model
, can be turned on to clear this deprecation and opt in to the new behaviour. This optional feature is enabled in blueprints as of v5.7.0
and will be removed in v6.0.0
. For more information, see the optional features guides.
For example:
import { Route } from '@ember/routing/route';
import { service } from '@ember/service';
export default class MyModelRoute extends Route {
@service store;
model({ my_model_id }) {
return this.store.findRecord('my-model', my_model_id);
}
}
For more background, read the RFC.
Deprecations Added in 5.9.0
Action helper and modifier
Scenario: action
is passed a string
Before:
<button type="button" {{action "plusOne"}}>
Click Me
</button>
After:
<button type="button" {{on 'click' this.plusOne}}>
Click Me
</button>
or, if plusOne
is passed in as an argument:
<button type="button" {{on 'click' @plusOne}}>
Click Me
</button>
If the plusOne
action is in an actions object, it needs to move out:
For Glimmer components
Before:
import Component from '@glimmer/component';
export default class Demo extends Component {
actions = {
plusOne() {
/* ... */
}
}
}
After:
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class Demo extends Component {
@action
plusOne() {
/* ... */
}
}
or
For Classic Components with native classes
Before:
import Component from '@ember/component';
export default class Demo extends Component {
doMath() {
this.send('plusOne');
}
actions = {
plusOne() {
/* ... */
}
}
}
After:
import Component from '@ember/component';
import { action } from '@ember/object';
export default class Demo extends Component {
doMath() {
this.plusOne();
}
@action
plusOne() {
/* ... */
}
}
or
For Classic Components with EmberObject.extend
Before:
import Component from '@ember/component';
export default Component.extend({
actions: {
plusOne() {
/* ... */
}
}
})
After:
import Component from '@ember/component';
import { action } from '@ember/object';
export default Component.extend({
plusOne: action(function() {
/* ... */
}),
})
If (action)
or {{action}}
is passed a string, it's possible that the referenced method is declared on the caller, and not the immediate component -- that is, (action)
and {{action}}
bubble up the render tree from route templates -> controllers -> routes.
Note that @action
is completely different from (action)
or {{action}}
(and is partly a motivator for deprecating (action)
and {{action}}
, to reduce ambiguity).
@action
binds the this
on the method to the instance of the class.
Scenario: action
is passed a function reference
Before:
<SomeComponent @update={{action this.plusOne}} />
After
<SomeComponent @update={{this.plusOne}} />
Scenario: action
is passed parameters
Before:
<SomeComponent @update={{action this.plus 1}} />
After:
<SomeComponent @update={{fn this.plus 1}} />
Scenario: action
is used with mut
Before:
<SomeComponent @update={{action (mut @value.property}} />
After:
// parent.js
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class SomeComponent extends Component {
@action
handleUpdate(value) {
this.args.property = value;
}
}
{{! parent.hbs }}
<SomeComponent @update={{this.handleUpdate}} />
Related, Combining function arguments with action functions
For more background, read the RFC
Deprecations Added in 5.10.0
Component Template Resolving
There are two types of paths to migrate off the old layouts
- use a currently supported multi-file layout (keeping separate
js
,ts
, andhbs
files) - migrate the component entirely to the latest component format,
gjs
,gts
, (aka<template>
)
There are some tools to help with this:
Specifically, these layouts are no longer supported:
Classic | Pods |
---|---|
|
|
The above example(s) can be migrated to:
{app,addon}/
components/
foo.js
foo.hbs
namespace/
bar.js
bar.hbs
Or using --component-structure=nested
{app,addon}/
components/
foo/
index.js
index.hbs
namespace/
bar/
index.js
index.hbs
Note, however, that classic components importing the layout
and setting it on an @ember/component
will still work.
The key thing being deprecated is the runtime resolution of templates, so if there is an import involved, there is no runtime resolution.
Deprecate array prototype extensions
Ember historically extended the prototypes of native Javascript arrays to implement Ember.Enumerable
, Ember.MutableEnumerable
, Ember.MutableArray
, Ember.Array
. As of v5, the usages of array prototype extensions are deprecated.
For convenient functions like filterBy
, compact
, you can directly convert to use native array methods.
For mutation functions (like pushObject
, replace
) or observable properties (firstObject
, lastObject
), in order to keep the reactivity, you should take following steps:
- convert the array either to a new
@tracked
property, or useTrackedArray
fromtracked-built-ins
; - use array native methods;
- fully test to make sure the reactivity is maintained.
Convenient Functions
For convenient functions like filterBy
, compact
, you can directly convert to use native array methods. This includes following (a list from EmberArray
methods):
any
Before:
someArray.any(callbackFn);
After:
someArray.some(callbackFn);
compact
Before:
someArray.compact();
After:
someArray.filter(val => val !== undefined && val !== null);
filterBy
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filterBy('food', 'beans'); // [{ food: 'beans', isFruit: false }]
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filter(el => el.food === 'beans'); // [{ food: 'beans', isFruit: false }]
findBy
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.findBy('isFruit'); // { food: 'apple', isFruit: true }
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.find(el => el.isFruit); // { food: 'apple', isFruit: true }
getEach
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.getEach('food'); // ['apple', 'beans']
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.map(el => el.food); // ['apple', 'beans']
invoke
Before:
class Person {
name;
constructor(name) {
this.name = name;
}
greet(prefix = 'Hello') {
return `${prefix} ${this.name}`;
}
}
[new Person('Tom'), new Person('Joe')].invoke('greet', 'Hi'); // ['Hi Tom', 'Hi Joe']
After:
class Person {
name;
constructor(name) {
this.name = name;
}
greet(prefix = 'Hello') {
return `${prefix} ${this.name}`;
}
}
[new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe']
isAny
Before
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.isAny('isFruit'); // true
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.some(el => el.isFruit); // true
isEvery
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.isEvery('isFruit'); // false
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.every(el => el.isFruit); // false
mapBy
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.mapBy('food'); // ['apple', 'beans']
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.map(el => el.food); // ['apple', 'beans']
objectAt
Before
const someArray = [1, 2, 3, undefined];
someArray.objectAt(1); // 2
After:
const someArray = [1, 2, 3, undefined];
someArray[1] // 2
objectsAt
Before:
const someArray = [1, 2, 3, undefined];
someArray.objectsAt([1, 2]); // [2, 3]
After:
const someArray = [1, 2, 3, undefined];
[1, 2].map(index => someArray[index]); //[2, 3]
reject
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.reject(el => el.isFruit); // [{ food: 'beans', isFruit: false }]
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }]
rejectBy
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.rejectBy('isFruit'); // [{ food: 'beans', isFruit: false }]
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }]
sortBy
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
[...someArray].sort((a, b) => {
return a.food?.localeCompare(b.food)
? a.food?.localeCompare(b.food)
: a.isFruit - b.isFruit;
}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
toArray
Before:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
After:
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
[...someArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
uniq
Before:
const someArray = [1, 2, 3, undefined, 3];
someArray.uniq(); // [1, 2, 3, undefined]
After:
const someArray = [1, 2, 3, undefined, 3];
[...new Set(someArray)] // [1, 2, 3, undefined]
uniqBy
Before:
const someArray = [{ food: 'apple' }, { food: 'beans' }, { food: 'apple' }];
someArray.uniqBy('food'); // [{ food: 'apple' }, { food: 'beans' }]
After:
const someArray = [{ food: 'apple' }, { food: 'beans' }, { food: 'apple' }];
someArray.reduce(
(unique, item) => {
if (!unique.find(i => item.food === i.food)) {
unique.push(item);
}
return unique;
},
[]
); // [{ food: 'apple' }, { food: 'beans' }]
You may also instead rely on methods from another library like lodash. Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested.
Some special cases
without
Before
const someArray = ['a', 'b', 'c'];
someArray.without('a'); // ['b', 'c']
After
const someArray = ['a', 'b', 'c'];
someArray.filter(el => el !== 'a'); // ['b', 'c']
Please make sure without
reactivity is fully tested.
setEach
setEach
method internally implements set
which responds to reactivity. You can either also use set
or convert to @tracked
properties.
Before
const items = [{ name: 'Joe' }, { name: 'Matt' }];
items.setEach('zipCode', '10011'); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }]
After
// use `set`
import { set } from '@ember/object';
const items = [{ name: 'Joe' }, { name: 'Matt' }];
items.forEach(item => {
set(item, 'zipCode', '10011');
}); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }]
or
// use `@tracked`
import { tracked } from '@glimmer/tracking';
class Person {
name;
@tracked zipCode;
constructor({ name, zipCode }) {
this.name = name;
this.zipCode = zipCode;
}
}
const items = new TrackedArray([
new Person({ name: 'Joe' }),
new Person({ name: 'Matt' }),
]);
items.forEach(item => {
item.zipCode = '10011';
}); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }]
Observable Properties
firstObject
, lastObject
are observable properties. Changing directly from firstObject
to at(0)
or [0]
might cause issues that the properties are no longer reactive.
Used in template
If the firstObject
and lastObject
are used in a template, you can convert to use get
helper safely as get
helper handles the reactivity already.
Before
<Foo @bar={{@list.firstObject.name}} />
After
<Foo @bar={{get @list '0.name'}} />
You can also leverage fixers provided by ember-template-lint/no-array-prototype-extensions
.
Used in js
If the firstObject
and lastObject
are used in js files and you used them in an observable way, you will need to convert the accessors to @tracked
array or TrackedArray
.
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
// lastObj will change when `someAction` is executed
get lastObj() {
return this.abc.lastObject;
}
@action
someAction(value) {
this.abc.pushObject(value);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
get lastObj() {
return this.abc.at(-1);
}
@action
someAction(value) {
this.abc.push(value);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = [];
get lastObj() {
return this.abc.at(-1);
}
@action
someAction(value) {
this.abc = [...this.abc, value];
}
}
Mutation methods
Mutation methods are observable-based, which means you should always convert the accessors to @tracked
or TrackedArray
in order to maintain the reactivity. This includes following (a list from MutableArray
methods):
addObject
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
addObject(value) {
this.abc.addObject(value);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
addObject(value) {
if (!this.abc.includes(value)) {
this.abc.push(value);
}
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
addObject(value) {
if (!this.abc.includes(value)) {
this.abc = [...this.abc, value];
}
}
}
addObjects
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
addObjects(value) {
this.abc.addObjects(value);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
_addObject(value) {
if (!this.abc.includes(value)) {
this.abc.push(value);
}
}
@action
addObjects(values) {
values.forEach(v => this._addObject(v))
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
_addObject(value) {
if (!this.abc.includes(value)) {
this.abc = [...this.abc, value];
}
}
@action
addObjects(values) {
values.forEach(v => this._addObject(v))
}
}
clear
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
clear(value) {
this.abc.clear();
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
clear(value) {
this.abc.splice(0, this.abc.length);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
clear() {
this.abc = [];
}
}
insertAt
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
insertAt(idx, value) {
this.abc.insertAt(idx, value);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
insertAt(idx, value) {
this.abc.splice(idx, 0, value);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
insertAt(idx, value) {
this.abc = [...this.abc.slice(0, idx), value, this.abc.slice(this.abc.length - idx)]
}
}
popObject
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
popObject() {
this.abc.popObject();
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
popObject() {
this.abc.pop();
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
popObject() {
this.abc.pop();
this.abc = [...this.abc];
}
}
pushObject
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
pushObject(value) {
this.abc.pushObject(value);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
pushObject(value) {
this.abc.push(value);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
pushObject(value) {
this.abc = [...this.abc, value];
}
}
pushObjects
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
pushObjects(values) {
this.abc.pushObjects(values);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
pushObjects(values) {
this.abc.push(...values);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
pushObjects(values) {
this.abc = [...this.abc, ...values];
}
}
removeAt
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
removeAt(start, len) {
this.abc.removeAt(start, len);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
removeAt(start, len) {
this.abc.splice(start, len);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
removeAt(start, len) {
this.abc.splice(start, len);
this.abc = this.abc;
}
}
removeObject
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
removeObject(value) {
this.abc.removeObject(value);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
removeObject(value) {
this.abc.filter(item => item !== value);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
removeObject(value) {
let loc = this.abc.length || 0;
while (--loc >= 0) {
let curValue = this.abc.at(loc);
if (curValue === value) {
this.abc.splice(loc, 1);
}
}
this.abc = [...this.abc];
}
}
removeObjects
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
removeObjects(values) {
this.abc.removeObjects(values);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
_removeObject(value) {
let loc = this.abc.length || 0;
while (--loc >= 0) {
let curValue = this.abc.at(loc);
if (curValue === value) {
this.abc.splice(loc, 1);
}
}
}
@action
removeObjects(values) {
values.forEach(v => {
this._removeObject(v);
});
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
_removeObject(value) {
let loc = this.abc.length || 0;
while (--loc >= 0) {
let curValue = this.abc.at(loc);
if (curValue === value) {
this.abc.splice(loc, 1);
}
}
this.abc = [...this.abc];
}
@action
removeObjects(values) {
values.forEach(v => {
this._removeObject(v);
})
}
}
replace
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
replace(idx, len, values) {
this.abc.replace(idx, len, values);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
replace(idx, len, values) {
this.abc.splice(idx, len, ...values);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
replace(idx, len, values) {
this.abc.splice(idx, len, ...values);
this.abc = [...this.abc];
}
}
reverseObjects
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
reverseObjects() {
this.abc.reverseObjects();
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
reverseObjects() {
this.abc.reverse();
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
reverseObjects() {
this.abc = [...this.abc.reverse()];
}
}
setObjects
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
setObjects(values) {
this.abc.setObjects(values);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
setObjects(values) {
this.abc.splice(0, this.abc.length, ...values);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
setObjects(values) {
this.abc = [...values];
}
}
shiftObject
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
shiftObject() {
this.abc.shiftObject();
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
shiftObject() {
this.abc.shift();
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
shiftObject() {
this.abc.shift();
this.abc = [...this.abc]
}
}
unshiftObject
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
unshiftObject(obj) {
this.abc.unshiftObject(obj);
}
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
unshiftObject(obj) {
this.abc.unshift(obj);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
unshiftObject(obj) {
this.abc.unshift(obj);
this.abc = [...this.abc];
}
}
unshiftObjects
Before
import Component from '@glimmer/component';
export default class SampleComponent extends Component {
abc = ['x', 'y', 'z', 'x'];
@action
unshiftObjects(objs) {
this.abc.unshiftObjects(objs);
}
After
// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';
export default class SampleComponent extends Component {
abc = new TrackedArray(['x', 'y', 'z', 'x']);
@action
unshiftObjects(objs) {
this.abc.unshift(...objs);
}
}
or
// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class SampleComponent extends Component {
@tracked abc = ['x', 'y', 'z', 'x'];
@action
unshiftObjects(objs) {
this.abc.unshift(...objs)
this.abc = [...this.abc];
}
}
It's always recommended to reference the existing implementation of the method you are trying to convert. This can make sure functionalities are kept as it was. Implementation details can be found in MutableArray
, for example removeObject
.