A random collection of Ember computed macros. All the macros are composable, meaning you can nest them to your heart's content, like so:
result: conditional(and(not('value1'), 'value2'), sum('value3', 1), collect('value4', toUpper('value5'))) // lisp much?
The API is not final until 1.0. I will be adding aliases as I think of better names for things, and possibly breaking or removing existing macros.
If you have any opinions or want a new macro added, just ask! Or feel free to submit a pull request.
ember install ember-awesome-macros
import nameOfMacro from 'ember-awesome-macros/name-of-macro';
// or
import { nameOfMacro } from 'ember-awesome-macros';
any
array
collect
compact
contains
every
filterBy
filter
findBy
find
first
includes
indexOf
isAny
isEvery
join
lastIndexOf
last
mapBy
map
objectAt
reduce
slice
uniqBy
uniq
without
camelize
capitalize
classify
dasherize
decamelize
htmlSafe
split
substr
substring
tag
toLower
toUpper
underscore
alias for sum
same as Ember.computed.and
, but allows composing
source1: false,
source2: true,
source3: false,
value1: and('source1', 'source2', 'source3'), // false
value2: and(not('source1'), 'source2', not('source3')) // true
wraps Ember.MutableArray.any
, allows composing
array: Ember.A([1, 2]),
value1: any('array', val => val === 2), // true
value2: any('array', val => val === 3) // false
alias for collect
wraps Ember.String.camelize
, allows composing
originalValue: 'test-string',
newValue: camelize('originalValue') // 'testString'
wraps Ember.String.capitalize
, allows composing
originalValue: 'test string',
newValue: capitalize('originalValue') // 'Test string'
wraps Ember.String.classify
, allows composing
originalValue: 'test string',
newValue: classify('originalValue') // 'TestString'
same as Ember.computed.collect
, but allows composing
source1: 'my value 1',
source2: 'my value 2',
value: collect('source1', collect('source2')), // ['my value 1', ['my value 2']]
wraps Ember.MutableArray.compact
, allows composing
array: Ember.A([1, 2, null]),
value: compact('array') // [1, 2]
functions like Ember.computed
, but is composing friendly
key1: '1,2',
// your callback is passed the resolved values
computed1: computed('key1', value => {
console.log(value); // '1,2'
// do something else
}),
// you can compose your macros
computed2: computed(split('key1', raw(',')), value => {
console.log(value); // [1, 2]
// do something else
}),
// you can use enumerable helpers and property expansion
key2: [1, 2],
computed3: computed('key2.[]', value => {
console.log(value); // [1, 2]
// do something else
}),
key3: [{ key4: 1 }, { key4: 2 }],
computed4: computed('key3.@each.key4', value => {
console.log(value); // [{ key4: 1 }, { key4: 2 }]
// do something else
}),
key5: [{ key6: 1 }, { key7: 2 }],
computed5: computed('key5.{key6,key7}', (value1, value2) => {
console.log(value1); // 1
console.log(value2); // 2
// do something else
})
implements the ternary operator, allows composing
condition1: true,
condition2: false,
expr1: 'my value 1',
expr2: 'my value 2',
value1: conditional('condition1', 'expr1', 'expr2'), // 'my value 1'
value2: conditional('condition2', 'expr1', 'expr2'), // 'my value 2'
value3: conditional(or('condition1', 'condition2'), raw('my value 1'), raw('my value 2')) // 'my value 1'
alias for includes
wraps Ember.String.dasherize
, allows composing
originalValue: 'TestString',
newValue: dasherize('originalValue') // 'test-string'
wraps Ember.String.decamelize
, allows composing
originalValue: 'TestString',
newValue: decamelize('originalValue') // 'test_string'
true if source is undefined
source1: undefined,
source2: false,
source3: 'my value',
value1: defaultTrue('source1'), // true
value2: defaultTrue('source2'), // false
value3: defaultTrue('source3') // 'my value'
subtracts numbers
source1: 3,
source2: 2,
source3: 1,
value1: difference('source1', 'source2', 'source3'), // 0
value2: difference('source2', difference('source2', 'source3')) // 2
alias for quotient
alias for equal
like Ember.computed.equal
, but uses dependent properties on both sides
and allows composing
source1: 'my value',
source2: 'my other value',
source3: 'my value',
value1: equal('source1', 'source2'), // false
value2: equal('source1', 'source3') // true
wraps Ember.MutableArray.every
, allows composing
array: Ember.A([1, 1]),
value1: any('array', val => val === 1), // true
value2: any('array', val => val === 2) // false
wraps Ember.MutableArray.filterBy
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value: filterBy('array', 'key', 2) // [{ test: 2 }]
wraps Ember.MutableArray.filter
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
value: filter('array', item => item.test === 2) // [{ test: 2 }]
wraps Ember.MutableArray.findBy
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value: findBy('array', 'key', 2) // { test: 2 }
wraps Ember.MutableArray.find
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
value: find('array', item => item.test === 2) // { test: 2 }
get the first item of an array
array: ['1', '2'],
string: '1, 2',
example: first('array'), // '1'
composingExample: first(split('string', raw(', '))) // '1'
get a variable property name from an object
key: 'modelProperty',
model: {
modelProperty: 'my value'
},
value: getBy('model', 'key') // 'my value'
like Ember.computed.gt
, but uses dependent properties on both sides
and allows composing
source1: 1,
source2: 2,
source3: 1,
value1: gt('source1', 'source2'), // false
value2: gt('source1', 'source3'), // false
value3: gt('source2', 'source3') // true
like Ember.computed.gte
, but uses dependent properties on both sides
and allows composing
source1: 1,
source2: 2,
source3: 1,
value1: gte('source1', 'source2'), // false
value2: gte('source1', 'source3'), // true
value3: gte('source2', 'source3') // true
build a hash out of computed properties, allows composing
source1: 'my value 1',
source2: 'my value 2',
value1: hash({
prop1: 'source1',
prop2: hash({
prop: 'source2'
})
}), // { prop1: 'my value 1', prop2: { prop: 'my value 2' } }
// you can also build the hash using property key names
value2: hash('source1', 'source2'), // { source1: 'my value 1', source2: 'my value 2' }
// or you can mix and match, the result will be merged
value3: hash('source1', { prop2: 'source2' }) // { source1: 'my value 1', prop2: 'my value 2' }
wraps Ember.String.htmlSafe
, allows composing
originalValue: '<input>',
newValue: htmlSafe('originalValue') // will not be escaped
implements Array.prototype.includes()
, allows composing
array: Ember.A(['my value 1', 'my value 2']),
source1: 'my value 2',
source2: 'my value 3',
value1: includes('array', 'source1'), // true
value2: includes('array', 'source2'), // false
value3: includes(collect(raw('my value 1'), raw('my value 2')), raw('my value 1')) // true
wraps Array.prototype.indexOf
, allows composing
array: [2, 5, 9, 2],
value1: indexOf('array', 2), // 0
value2: indexOf('array', 2, 2) // 3
wraps instanceof
operator
key1: {},
key2: false,
key3: '',
value1: instanceOf('key1', Object), // true
value2: instanceOf(or('key2', 'key3'), String) // true
wraps Ember.Enumerable.isAny
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value1: 2,
value2: 3,
result1: isAny('array', 'key', 'value1'), // true
result2: isAny('array', 'key', 'value2') // false
wraps Ember.Enumerable.isEvery
, allows composing
array1: Ember.A([{ test: 1 }, { test: 1 }]),
key: 'test',
value1: 1,
value2: 2,
result1: isEvery('array', 'key', 'value1'), // true
result2: isEvery('array', 'key', 'value2') // false
wraps Ember.String.isHTMLSafe
, allows composing
source1: '<input>',
source2: htmlSafe('<input>'),
value1: isHtmlSafe('source1'), // false
value2: isHtmlSafe('source2') // true
implements Array.prototype.join()
, allows composing
array: Ember.A(['1', '2']),
separator: ', ',
value1: join('values', 'separator'), // '1, 2'
value2: join(collect(raw('1'), raw('2')), raw(', ')) // '1, 2'
wraps Array.prototype.lastIndexOf
, allows composing
array: [2, 5, 9, 2],
value1: lastIndexOf('array', 2), // 3
value2: lastIndexOf('array', 2, 2) // 0
get the last item of an array
array: ['1', '2'],
string: '1, 2',
example: last('array'), // '2'
composingExample: last(split('string', raw(', '))) // '2'
like Ember.computed.lt
, but uses dependent properties on both sides
and allows composing
source1: 1,
source2: 2,
source3: 1,
value1: lt('source1', 'source2'), // true
value2: lt('source1', 'source3'), // false
value3: lt('source2', 'source3') // false
like Ember.computed.lte
, but uses dependent properties on both sides
and allows composing
source1: 1,
source2: 2,
source3: 1,
value1: lte('source1', 'source2'), // true
value2: lte('source1', 'source3'), // true
value3: lte('source2', 'source3') // false
wraps Ember.MutableArray.mapBy
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value: mapBy('array', 'key') // [1, 2]
wraps Ember.MutableArray.map
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }]),
value: map('array', item => item.test) // [1, 2]
exposes all Math
functions
source1: 2.2,
source2: 2.7,
value1: math.ceil('source1'), // 3
value2: math.floor(sum('source1', 'source2')) // 4
alias for product
same as Ember.computed.not
, but allows composing
source1: true,
source2: false,
value1: not('source1'), // false
value2: not(and('source1', 'source2')) // true
implements http://emberjs.com/api/classes/Ember.MutableArray.html#method_objectAt
, allows composing
array: Ember.A(['my value']),
source1: 0,
source2: 1,
value1: objectAt('array', 'source1'), // 'my value'
value2: objectAt('array', 'source2'), // undefined
value3: objectAt(collect(raw('my value 1')), raw(0)) // 'my value'
same as Ember.computed.or
, but allows composing
source1: true,
source2: false,
source3: true,
value1: or('source1', 'source2', 'source3'), // true
value2: or(not('source1'), 'source2', not('source3')) // false
wraps parseFloat
, allows composing
string1: '12.34',
string2: '12',
string3: '34',
example: parseFloat('string1'), // 12.34
composingExample: parseFloat(tag`${'string2'}.${'string3'}`) // 12.34
wraps parseInt
, allows composing
string: '123',
example: parseInt('string'), // 123
composingExample: parseInt(substr('string', 1), 8) // 19
multiplies numbers
source1: 1,
source2: 2,
source3: 3,
value1: product('source1', 'source2', 'source3'), // 6
value2: product('source2', product('source2', 'source3')) // 6
combines promises using RSVP.all
promise1: computed(function() {
return RSVP.resolve('value1');
}),
promise2: computed(function() {
return RSVP.resolve('value2');
}),
promise: promiseAll('promise1', 'promise2') // resolves to ['value1', 'value2']
wraps a promise in the equivalent of DS.PromiseArray
(ArrayProxy
and PromiseProxyMixin
)
productsPromise: computed(function() {
return this.store.findAll('product');
}),
products: promiseArray('productsPromise')
can also wrap a computed property
products: promiseArray(computed(function() {
return this.store.findAll('product');
}))
combines promises using RSVP.hash
promise1: computed(function() {
return RSVP.resolve('value1');
}),
promise2: computed(function() {
return RSVP.resolve('value2');
}),
promise: promiseHash('promise1', 'promise2') // resolves to { promise1: 'value1', promise2: 'value2' }
wraps a promise in the equivalent of DS.PromiseObject
(ObjectProxy
and PromiseProxyMixin
)
productPromise: computed(function() {
return this.store.findRecord('product', 1);
}),
product: promiseObject('productPromise')
can also wrap a computed property
product: promiseObject(computed(function() {
return this.store.findRecord('product', 1);
}))
wraps a value in an RSVP.resolve
key1: 'my value',
promise1: promiseResolve('key1'), // a resolved promise if you need it
key2: computed(function() {
return this.store.findRecord('user');
}),
promise2: promiseResolve(conditional('someBool', 'key1', 'key2')) // resolve an object if you don't know if it is a promise or not
subtracts numbers
source1: 3,
source2: 2,
source3: 1,
value1: quotient('source1', 'source2', 'source3'), // 1.5
value2: quotient('source2', quotient('source2', 'source3')) // 1.5
a helper if you want to use "literals" in your macros
value1: equal('key1', raw('my value 1')),
value2: indexOf('key2', raw('my value 2'))
or you want to get fancy with composing
source: 'my computed value',
value: hash({
prop1: 'source',
prop2: raw('my raw value')
}) // { prop1: 'my computed value', prop2: 'my raw value' }
wraps Array.prototype.reduce
, allows composing
array: ['one', 'two'],
value1: reduce('array', (obj, cur, i) => {
obj[cur] = i;
return obj;
}, {}), // { one: 0, two: 1 }
string: 'one, two',
value2: reduce(split('string', raw(', ')), (obj, cur, i) => {
obj[cur] = i;
return obj;
}, {}) // { one: 0, two: 1 }
wraps Array.prototype.slice
, allows composing
array: [1, 2, 3],
value1: slice('array', 1), // [2, 3]
value2: slice('array', difference('array.length', 1)) // [3]
implements String.prototype.split()
, allows composing
source: 'val1,val2',
key: ',',
value1: split('source', 'key'), // ['val1', 'val2']
value2: split('source', raw(',')) // ['val1', 'val2']
wraps String.prototype.substr
, allows composing
string1: 'abcxyz',
string2: 'abc',
string3: 'xyz',
example: substr('string1', 2, 2), // 'cx'
composingExample: substr(tag`${'string2'}${'string3'}`, 2, 2) // 'cx'
wraps String.prototype.substring
, allows composing
string1: 'abcxyz',
string2: 'abc',
string3: 'xyz',
example: substring('string1', 2, 4), // 'cx'
composingExample: substring(tag`${'string2'}${'string3'}`, 2, 4) // 'cx'
alias for difference
adds numbers
source1: 1,
source2: 2,
source3: 3,
value1: sum('source1', 'source2', 'source3'), // 6
value2: sum('source2', sum('source2', 'source3')) // 6
use a tagged template literal as a computed macro, allows composing
source: 'two',
value1: tag`one ${'source'} three`, // 'one two three'
value2: tag`one ${toUpper('source')} three` // 'one TWO three'
wraps String.prototype.toLowerCase
, allows composing
originalValue: 'TestString',
newValue: toLower('originalValue') // 'teststring'
wraps String.prototype.toUpperCase
, allows composing
originalValue: 'TestString',
newValue: toUpper('originalValue') // 'TESTSTRING'
wraps typeOf
operator
key1: {},
key2: false,
key3: '',
value1: typeOf('key1'), // 'object'
value2: typeOf(or('key2', 'key3')) // 'string'
wraps Ember.String.underscore
, allows composing
originalValue: 'TestString',
newValue: underscore('originalValue') // 'test_string'
wraps Ember.MutableArray.uniqBy
, allows composing
array: Ember.A([{ test: 1 }, { test: 2 }, { test: 2 }]),
key: 'test',
value: uniqBy('array', 'key') // [{ test: 1 }, { test: 2 }]
wraps Ember.MutableArray.uniq
, allows composing
array: Ember.A([1, 2, 2]),
value: uniq('array') // [1, 2]
wraps Ember.Enumerable.without
, allows composing
array: Ember.A([1, 2, 3]),
value1: without('array', 2), // [1, 3]
value2: without('array', objectAt(1)) // [1, 3]
since all the macros are read-only, use this to bring back setting capability
value1: writable(and('key1', 'key2')), // setting this replaces the macro with your value
value2: writable(and('key1', 'key2'), {
set() {
// do something
return 'new value';
}
}), // setting this will not overwrite your macro
value3: writable(and('key1', 'key2'), function() {
// do something
return 'new value';
}), // same as above, but shorthand