Class ComputedProperty

public

@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
17
import { computed, set } from '@ember/object';

class Person {
  constructor(firstName, lastName) {
    set(this, 'firstName', firstName);
    set(this, 'lastName', lastName);
  }

  @computed('firstName', 'lastName')
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
});

let tom = new Person('Tom', 'Dale');

tom.fullName; // 'Tom Dale'

You can also provide a setter, which will be used when updating the computed property. Ember's set function must be used to update the property since it will also notify observers of the property:

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
import { computed, set } from '@ember/object';

class Person {
  constructor(firstName, lastName) {
    set(this, 'firstName', firstName);
    set(this, 'lastName', lastName);
  }

  @computed('firstName', 'lastName')
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  set fullName(value) {
    let [firstName, lastName] = value.split(' ');

    set(this, 'firstName', firstName);
    set(this, 'lastName', lastName);
  }
});

let person = new Person();

set(person, 'fullName', 'Peter Wagenet');
person.firstName; // 'Peter'
person.lastName;  // 'Wagenet'

You can also pass a getter function or object with get and set functions as the last argument to the computed decorator. This allows you to define computed property macros:

1
2
3
4
5
6
7
8
9
10
11
12
import { computed } from '@ember/object';

function join(...keys) {
  return computed(...keys, function() {
    return keys.map(key => this[key]).join(' ');
  });
}

class Person {
  @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
33
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);
  }

  @fullNameMacro('firstName', '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
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);
  }

  @computed('firstName', '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:

Show:

Methods

Properties

No documented items

Events

No documented items