ember-computed-decorators
v0.1.3ember-computed-decorators
This addon allows usage of the proposed decorator syntax, and passes the specified dependent keys into your computed function making your computed properties much DRY'er.
More details:
Usage
Babel Setup
To use ember-computed-decorators you must update Babel's configuration to allow usage of the decorator proposal.
As of Babel 5.1.0 the following should be all you need in your ember-cli application:
- pre-1.13.0
// Brocfile.js
var app = new EmberApp({
babel: {
optional: ['es7.decorators']
}
});
- 1.13.x
// ember-cli-build.js
var app = new EmberApp({
babel: {
optional: ['es7.decorators']
}
});
Setup with addon
Add the following init
method to index.js
of your addon.
This should enable the decorators to work on the parent app/addon.
init: function(app) {
this._super.init && this._super.init.apply(this, arguments);
this.options = this.options || {};
this.options.babel = this.options.babel || {};
this.options.babel.optional = this.options.babel.optional || [];
if (this.options.babel.optional.indexOf('es7.decorators') === -1) {
this.options.babel.optional.push('es7.decorators');
}
}
Application Usage
With Dependent Keys
In your application where you would normally have:
foo: Ember.computed('someKey', 'otherKey', function() {
var someKey = this.get('someKey');
var otherKey = this.get('otherKey');
// Do Stuff
})
You replace with this:
import computed from 'ember-computed-decorators';
// ..... <snip> .....
@computed('someKey', 'otherKey')
foo(someKey, otherKey) {
// Do Stuff
}
Without Dependent Keys
foo: Ember.computed(function() {
// Do Stuff
})
You replace with this:
import computed from 'ember-computed-decorators';
// ..... <snip> .....
@computed
foo() {
// Do Stuff
}
"Real World"
import Ember from 'ember';
import computed from 'ember-computed-decorators';
export default Ember.Component.extend({
@computed('first', 'last')
name(first, last) {
return `${first} ${last}`;
}
});
"Real World get/set syntax"
import Ember from 'ember';
import computed from 'ember-computed-decorators';
export default Ember.Component.extend({
@computed('first', 'last')
name: {
get(first, last) {
return `${first} ${last}`;
},
set(value, first, last) {
// ...
}
}
});
"readOnly"
import Ember from 'ember';
import computed, { readOnly } from 'ember-computed-decorators';
export default Ember.Component.extend({
@readOnly
@computed('first', 'last')
name(first, last) {
return `${first} ${last}`;
}
});
Enumerables / Arrays
When a computed property key contains @each
, []
(enumerable) then the
argument that is passed to the get
or set
method will be the object at the
path up to the @each
or []
part.
import Ember from 'ember';
import computed from 'ember-computed-decorators';
export default Ember.Component.extend({
persons: [
{ first: 'David', last: 'Heinemeier Hansson' },
{ first: 'Aaron', last: 'Patterson' }
],
@computed('persons.@each.{first,last}')
names(persons) {
return persons.map((person) => `${person.first} ${person.last}`);
}
});
Property Expansion
When a computed property key contains {foo,bar}
then the arguments that are
passed to the get
or set
method will get the expanded properties.
import Ember from 'ember';
import computed from 'ember-computed-decorators';
export default Ember.Component.extend({
address: {
street: 'Pennsylvania Avenue',
number: 1600
},
@computed('address.{street,number}')
formattedStreet(street, number) {
return `${number} ${street}`;
}
});
ember-data
import DS from 'ember-data';
import {
attr,
hasMany
} from "ember-computed-decorators/ember-data";
export default DS.Model.extend({
@attr firstName,
@hasMany users
});
Computed Macros
All of the computed macros are also available for use.
alias
Creates a new property that is an alias for another property on an object. Calls to get or set this property behave as though they were called on the original property.
import Ember from 'ember';
import { alias } from 'ember-computed-decorators';
export default Ember.Component.extend({
person: {
first: 'Joe'
},
@alias('person.first') firstName
});
and
A computed property that performs a logical and on the original values for the provided dependent properties.
import Ember from 'ember';
import { and } from 'ember-computed-decorators';
export default Ember.Component.extend({
person: {
first: 'Joe'
},
@and('first', 'last') hasFullName // false
});
bool
A computed property that converts the provided dependent property into a boolean value.
import Ember from 'ember';
import { bool } from 'ember-computed-decorators';
export default Ember.Component.extend({
messageCount: 1,
@bool('messageCount') hasMessages // true
});
collect
A computed property that returns the array of values for the provided dependent properties.
import Ember from 'ember';
import { collect } from 'ember-computed-decorators';
export default Ember.Component.extend({
light: 'strobe',
lens: '35mm prime',
@collect('light', 'lens') equipment // ['strobe', '35mm prime']
});
empty
A computed property that returns true
if the value of the dependent property is null, an empty string, empty array, or empty function.
import Ember from 'ember';
import { empty } from 'ember-computed-decorators';
export default Ember.Component.extend({
items: Ember.A(['taco', 'burrito']),
@empty('items') isEmpty // false
});
equal
import Ember from 'ember';
import { equal } from 'ember-computed-decorators';
export default Ember.Component.extend({
state: 'sleepy',
@equal('state', 'sleepy') napTime // true
});
filter
Filters the array by the callback.
The callback method you provide should have the following signature. item is the current item in the iteration. index is the integer index of the current item in the iteration. array is the dependant array itself.
function(item, index, array)
import Ember from 'ember';
import { filter } from 'ember-computed-decorators';
export default Ember.Component.extend({
chores: Ember.A([
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]),
@filter('chores', function(chore, index, array) {
return !chore.done;
}) remainingChores // [{name: 'write more unit tests', done: false}]
});
filterBy
Filters the array by the property and value.
import Ember from 'ember';
import { filterBy } from 'ember-computed-decorators';
export default Ember.Component.extend({
chores: Ember.A([
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]),
@filterBy('chores', 'done', false) remainingChores // [{name: 'write more unit tests', done: false}]
});
gt
A computed property that returns true
if the provided dependent property is greater than the provided value.
import Ember from 'ember';
import { gt } from 'ember-computed-decorators';
export default Ember.Component.extend({
totalCats: 11,
@gt('totalCats', 10) isCatParty // true
});
gte
A computed property that returns true
if the provided dependent property is greater than or equal to the provided value.
import Ember from 'ember';
import { gte } from 'ember-computed-decorators';
export default Ember.Component.extend({
totalPlayers: 14,
@gte('totalPlayers', 14) hasEnoughPlayers // true
});
intersect
A computed property which returns a new array with all the duplicated elements from two or more dependent arrays.
import Ember from 'ember';
import { intersect } from 'ember-computed-decorators';
export default Ember.Component.extend({
likes: Ember.A([ 'tacos', 'puppies', 'pizza' ]),
foods: Ember.A(['tacos', 'pizza']),
@intersect('likes', 'foods') favoriteFoods // ['tacos', 'pizza']
});
lt
A computed property that returns true
if the provided dependent property is less than the provided value.
import Ember from 'ember';
import { lt } from 'ember-computed-decorators';
export default Ember.Component.extend({
totalDogs: 3,
@lt('totalDogs', 10) isDogParty // true
});
lte
A computed property that returns true
if the provided dependent property is less than or equal to the provided value.
import Ember from 'ember';
import { lte } from 'ember-computed-decorators';
export default Ember.Component.extend({
totalPlayers: 14,
@lte('totalPlayers', 14) hasEnoughPlayers // true
});
map
Returns an array mapped via the callback
The callback method you provide should have the following signature. item is the current item in the iteration. index is the integer index of the current item in the iteration.
function(item, index);
import Ember from 'ember';
import { map } from 'ember-computed-decorators';
export default Ember.Component.extend({
chores: Ember.A(['clean', 'write more unit tests']),
@map('chores', function(chore, index) {
return chore.toUpperCase() + '!';
}) loudChores // ['CLEAN!', 'WRITE MORE UNIT TESTS!']
});
mapBy
Returns an array mapped to the specified key.
import Ember from 'ember';
import { mapBy } from 'ember-computed-decorators';
export default Ember.Component.extend({
people: Ember.A([
{name: "George", age: 5},
{name: "Stella", age: 10},
{name: "Violet", age: 7}
]),
@mapBy('people', 'age') ages // [5, 10, 7]
});
match
A computed property which matches the original value for the dependent property against a given RegExp, returning true
if they values matches the RegExp and false
if it does not.
import Ember from 'ember';
import { match } from 'ember-computed-decorators';
export default Ember.Component.extend({
email: '[email protected]',
@match('email', /^.+@.+\..+$/) validEmail
});
max
A computed property that calculates the maximum value in the dependent array. This will return -Infinity
when the dependent array is empty.
import Ember from 'ember';
import { max } from 'ember-computed-decorators';
export default Ember.Component.extend({
values: Ember.A([1, 2, 5, 10]),
@max('values') maxValue // 10
});
min
A computed property that calculates the minimum value in the dependent array. This will return Infinity
when the dependent array is empty.
import Ember from 'ember';
import { min } from 'ember-computed-decorators';
export default Ember.Component.extend({
values: Ember.A([1, 2, 5, 10]),
@min('values') minValue // 1
});
none
A computed property that returns true if the value of the dependent property is null or undefined. This avoids errors from JSLint complaining about use of ==
, which can be technically confusing.
import Ember from 'ember';
import { none } from 'ember-computed-decorators';
export default Ember.Component.extend({
firstName: null,
@none('firstName') isNameless // true until firstName is defined
});
not
A computed property that returns the inverse boolean value of the original value for the dependent property.
import Ember from 'ember';
import { not } from 'ember-computed-decorators';
export default Ember.Component.extend({
loggedIn: false,
@not('loggedIn') isAnonymous // true
});
notEmpty
A computed property that returns true
if the value of the dependent property is NOT null, an empty string, empty array, or empty function.
import Ember from 'ember';
import { notEmpty } from 'ember-computed-decorators';
export default Ember.Component.extend({
groceryBag: Ember.A(['milk', 'eggs', 'apples']),
@notEmpty('groceryBag') hasGroceriesToPutAway // true
});
oneWay
Where computed.alias
aliases get
and set
, and allows for bidirectional data flow, computed.oneWay
only provides an aliased get
. The set
will not mutate the upstream property, rather causes the current property to become the value set. This causes the downstream property to permanently diverge from the upstream property.
import Ember from 'ember';
import { oneWay } from 'ember-computed-decorators';
export default Ember.Component.extend({
firstName: 'Joe',
@oneWay('firstName') originalName // will always be 'Joe'
});
or
A computed property which performs a logical or on the original values for the provided dependent properties.
import Ember from 'ember';
import { or } from 'ember-computed-decorators';
export default Ember.Component.extend({
hasJacket: true,
hasUmbrella: false,
@or('hasJacket', 'hasUmbrella') isReadyForRain // true
});
readOnly
Where computed.oneWay
provides oneWay bindings, computed.readOnly
provides a readOnly one way binding. Very often when using computed.oneWay
one does not also want changes to propagate back up, as they will replace the value.
This prevents the reverse flow, and also throws an exception when it occurs.
import Ember from 'ember';
import { readOnly, alias } from 'ember-computed-decorators';
export default Ember.Component.extend({
@readOnly @alias('first') firstName // throws an error if set 'firstName' is attempted
});
reads
This is a more semantically meaningful alias of computed.oneWay
, whose name is somewhat ambiguous as to which direction the data flows.
import Ember from 'ember';
import { reads } from 'ember-computed-decorators';
export default Ember.Component.extend({
@reads('first') firstName
});
setDiff
A computed property which returns a new array with all the properties from the first dependent array that are not in the second dependent array.
import Ember from 'ember';
import { setDiff } from 'ember-computed-decorators';
export default Ember.Component.extend({
likes: Ember.A([ 'tacos', 'puppies', 'pizza' ]),
foods: Ember.A(['tacos', 'pizza']),
@setDiff('likes', 'foods') favoriteThingsThatArentFood // ['puppies']
});
sort
A computed property which returns a new array with all the properties from the first dependent array sorted based on a property or sort function.
The callback method you provide should have the following signature:
function(itemA, itemB);
itemA
the first item to compare.
itemB
the second item to compare.
This function should return negative number (e.g. -1
) when itemA
should come before itemB
. It should return positive number (e.g. 1
) when itemA
should come after itemB
. If the itemA
and itemB
are equal this function should return 0
.
Therefore, if this function is comparing some numeric values, simple itemA - itemB
or itemA.get( 'foo' ) - itemB.get( 'foo' )
can be used instead of series of if
.
import Ember from 'ember';
import { sort } from 'ember-computed-decorators';
export default Ember.Component.extend({
this.names = Ember.A([{name:'Link'},{name:'Zelda'},{name:'Gannon'},{name:'Navi'}]);
@sort('names', function(a, b){
if (a.name > b.name) {
return 1;
} else if (a.name < b.name) {
return -1;
}
return 0;
}) sortedNames // [{name:'Gannon'},{name:'Link'},{name:'Navi'},{name:'Zelda'}]
});
sum
A computed property that returns the sum of the value in the dependent array.
import Ember from 'ember';
import { sum } from 'ember-computed-decorators';
export default Ember.Component.extend({
values: Ember.A([1, 2, 3]),
@sum('values') total // 6
});
union
Alias for uniq.
import Ember from 'ember';
import { union } from 'ember-computed-decorators';
export default Ember.Component.extend({
likes: Ember.A([ 'tacos', 'puppies', 'pizza' ]),
foods: Ember.A(['tacos', 'pizza', 'ramen']),
@union('likes', 'foods') favorites // ['tacos', 'puppies', 'pizza', 'ramen']
});
uniq
A computed property which returns a new array with all the unique elements from one or more dependent arrays.
import Ember from 'ember';
import { uniq } from 'ember-computed-decorators';
export default Ember.Component.extend({
likes: Ember.A([ 'tacos', 'puppies', 'pizza' ]),
foods: Ember.A(['tacos', 'pizza', 'ramen']),
@uniq('likes', 'foods') favorites // ['tacos', 'puppies', 'pizza', 'ramen']
});
Installation
git clone <repository-url>
this repositorycd ember-computed-decorators
npm install
bower install
Running
ember serve
- Visit your app at http://localhost:4200.
Running Tests
npm test
(Runsember try:each
to test your addon against multiple Ember versions)ember test
ember test --server
Building
ember build
For more information on using ember-cli, visit https://ember-cli.com/.
Metadata
- MIT
- >= 0.10.0
- Robert Jackson
- released 5/8/2015