Class ComputedProperty
publicimport ComputedProperty from '@ember/object/computed'; |
@computed
is a decorator that turns a JavaScript getter and setter into a
computed property, which is a cached, trackable value. By default the getter
will only be called once and the result will be cached. You can specify
various properties that your computed property depends on. This will force the
cached result to be cleared if the dependencies are modified, and lazily recomputed the next time something asks for it.
In the following example we decorate a getter - fullName
- by calling
computed
with the property dependencies (firstName
and lastName
) as
arguments. The fullName
getter will be called once (regardless of how many
times it is accessed) as long as its dependencies do not change. Once
firstName
or lastName
are updated any future calls to fullName
will
incorporate the new values, and any watchers of the value such as templates
will be updated:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { computed, set } from '@ember/object'; class Person { constructor(firstName, lastName) { set(this, 'firstName', firstName); set(this, 'lastName', lastName); } .readOnly() get fullName() { return `${this.firstName} ${this.lastName}`; } }); let person = new Person(); person.set('fullName', 'Peter Wagenet'); // Uncaught Error: Cannot set read-only property "fullName" on object: <(...):emberXXX> |
Additional resources:
- Decorators RFC
- New CP syntax RFC
- New computed syntax explained in "Ember 1.12 released"
@join('firstName', 'lastName')
fullName;
}
Note that when defined this way, getters and setters receive the key of the property they are decorating as the first argument. Setters receive the value they are setting to as the second argument instead. Additionally, setters must return the value that should be cached:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import { computed, set } from '@ember/object'; function fullNameMacro(firstNameKey, lastNameKey) { return computed(firstNameKey, lastNameKey, { get() { return `${this[firstNameKey]} ${this[lastNameKey]}`; } set(key, value) { let [firstName, lastName] = value.split(' '); set(this, firstNameKey, firstName); set(this, lastNameKey, lastName); return value; } }); } class Person { constructor(firstName, lastName) { set(this, 'firstName', firstName); set(this, 'lastName', lastName); } fullName; }); let person = new Person(); set(person, 'fullName', 'Peter Wagenet'); person.firstName; // 'Peter' person.lastName; // 'Wagenet' |
Computed properties can also be used in classic classes. To do this, we provide the getter and setter as the last argument like we would for a macro, and we assign it to a property on the class definition. This is an anonymous computed macro:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import EmberObject, { computed, set } from '@ember/object'; let Person = EmberObject.extend({ // these will be supplied by `create` firstName: null, lastName: null, fullName: computed('firstName', 'lastName', { get() { return `${this.firstName} ${this.lastName}`; } set(key, value) { let [firstName, lastName] = value.split(' '); set(this, 'firstName', firstName); set(this, 'lastName', lastName); return value; } }) }); let tom = Person.create({ firstName: 'Tom', lastName: 'Dale' }); tom.get('fullName') // 'Tom Dale' |
You can overwrite computed property without setters with a normal property (no
longer computed) that won't change if dependencies change. You can also mark
computed property as .readOnly()
and block all attempts to set it.
1 2 3 4 5 6 7 |
import { computed, set } from '@ember/object'; class Person { constructor(firstName, lastName) { set(this, 'firstName', firstName); set(this, 'lastName', lastName); } |
meta (meta) public
Defined in packages/@ember/-internals/metal/lib/computed.ts:480
- meta
- Object
In some cases, you may want to annotate computed properties with additional metadata about how they function or what values they operate on. For example, computed property functions may close over variables that are then no longer available for introspection. You can pass a hash of these values to a computed property like this:
1 2 3 4 5 6 |
import { computed } from '@ember/object'; import Person from 'my-app/utils/person'; person: computed(function() { let personId = this.get('personId'); return Person.create({ id: personId }); }).meta({ type: Person }) |
The hash that you pass to the meta()
function will be saved on the
computed property descriptor under the _meta
key. Ember runtime
exposes a public API for retrieving these values from classes,
via the metaForProperty()
function.
property (path) ComputedProperty public
Defined in packages/@ember/-internals/metal/lib/computed.ts:421
- path
- String
- zero or more property paths
- returns
- ComputedProperty
- this
Sets the dependent keys on this computed property. Pass any number of arguments containing key paths that this computed property depends on.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import EmberObject, { computed } from '@ember/object'; let President = EmberObject.extend({ fullName: computed('firstName', 'lastName', function() { return this.get('firstName') + ' ' + this.get('lastName'); // Tell Ember that this computed property depends on firstName // and lastName }) }); let president = President.create({ firstName: 'Barack', lastName: 'Obama' }); president.get('fullName'); // 'Barack Obama' |
readOnly ComputedProperty public
Defined in packages/@ember/-internals/metal/lib/computed.ts:395
- returns
- ComputedProperty
- this
Call on a computed property to set it into read-only mode. When in this mode the computed property will throw an error when set.
1 2 3 4 5 6 7 8 |
import EmberObject, { computed } from '@ember/object'; let Person = EmberObject.extend({ guid: computed(function() { return 'guid-guid-guid'; }).readOnly() }); let person = Person.create(); person.set('guid', 'new-guid'); // will throw an exception |
volatile ComputedProperty public
Defined in packages/@ember/-internals/metal/lib/computed.ts:361
- returns
- ComputedProperty
- this
Call on a computed property to set it into non-cached mode. When in this mode the computed property will not automatically cache the return value. It also does not automatically fire any change events. You must manually notify any changes if you want to observe this property. Dependency keys have no effect on volatile properties as they are for cache invalidation and notification when cached value is invalidated.
1 2 3 4 5 6 |
import EmberObject, { computed } from '@ember/object'; let outsideService = EmberObject.extend({ value: computed(function() { return OutsideService.getValue(); }).volatile() }).create(); |