Skip to content

shamcode/ember-macro-helpers

 
 

Repository files navigation

ember-macro-helpers

Greenkeeper badge npm version Build Status Ember Version

Ember macro helpers for making your own fancy macros!

Check out the following projects to see this addon in use:

Contents

API

computed

computed behaves like Ember.computed with some extra benefits.

It will pass you resolved values:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';

export default Ember.Component.extend({
  key: 'my value',

  result: computed('key', {
    get(value) {
      console.log(value); // 'my value'
      // do something else
    },
    set(newValue, value) {
      console.log(newValue); // 'new value'
      console.log(value); // 'my value'
      return newValue;
    }
  }),

  actions {
    doSomething() {
      this.set('result', 'new value');
    }
  }
});

You can compose using any of Ember's built-in macros:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';

export default Ember.Component.extend({
  key1: false,
  key2: true,

  result: computed(Ember.computed.or('key1', 'key2'), value => {
    console.log(value); // true
    // do something else
  })
});

or you can compose using a macro library like ember-awesome-macros:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';
import conditional from 'ember-awesome-macros/conditional';
import sum from 'ember-awesome-macros/sum';
import difference from 'ember-awesome-macros/difference';

export default Ember.Component.extend({
  key1: 345678,
  key2: 785572,

  result: computed(conditional(gt('key1', 'key2'), sum('key1', 'key2'), difference('key1', 'key2')), value => {
    console.log(value); // -439894
    // do something else
  })
});

It respects enumerable helpers:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';

export default Ember.Component.extend({
  key1: [{ key2: 1 }, { key2: 2 }],

  computed1: computed('key1.[]', value => {
    console.log(value); // [{ key2: 1 }, { key2: 2 }]
    // do something else
  }),
  computed2: computed('[email protected]', value => {
    console.log(value); // [{ key2: 1 }, { key2: 2 }]
    // do something else
  }),
});

It resolves property expansion for you:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';

export default Ember.Component.extend({
  key1: { key2: 1, key3: 2 },

  result: computed('key1.{key2,key3}', (value1, value2) => {
    console.log(value1); // 1
    console.log(value2); // 2
    // do something else
  })
});

This is also your best friend if you want to make your own macros that support composing out-of-the-box.

For example, here is an implementation of a macro that adds two numbers together:

// app/macros/add.js
import computed from 'ember-macro-helpers/computed';

export default function(key1, key2) {
  // The incoming keys can be key strings, raw values, or other macros.
  // It makes no difference to you.
  // `computed` will resolve them for you.
  return computed(key1, key2, (value1, value2) => {
    // At this point, the keys no long matter.
    // You are provided the resolved values for you to perform your operation.
    return value1 + value2;
  });
}

Then you can use it like this:

import Ember from 'ember';
import add from 'my-app/macros/add';

export default Ember.Component.extend({
  key1: 12,
  key2: 34,
  key3: 56,

  result: add(add('key1', 'key2'), add('key3', 78)) // 180
});
createClassComputed

This creates a class-based computed. This is useful when not the value, but the key being watched is variable. It rewrites your computed property when needed.

See ember-classy-computed for the inspiration source.

If you want an array macro that will respond when someone changes the array property they want to watch:

// app/macros/filter-by.js
import createClassComputed from 'ember-macro-helpers/create-class-computed';
import computed from 'ember-macro-helpers/computed';

export default createClassComputed(
  // the first param is the observer list
  // it refers to incoming keys
  // the bool is whether a value change should recreate the macro
  [
    // the array key
    false,

    // the array property is dynamic, and is responsible for the macro being rewritten
    true,
    
    // 
    false
  ],
  // the second param is the callback function where you create your computed property
  // it is passed in the values of the properties you marked true above
  (array, key, value) => {
    // when `key` changes, we need to watch a new property on the array
    // since our computed property is now invalid, we need to create a new one
    return computed(`${array}.@each.${key}`, value, (array, value) => {
      return array.filterBy(key, value);
    });
  }
);

And then we consume this macro like normal:

import Ember from 'ember';
import filterBy from 'my-app/macros/filter-by';

export default Ember.Component.extend({
  myArray: Ember.A([
    Ember.Object.create({ myProp: 0 }),
    Ember.Object.create({ myProp: 1 })
  ]),

  // this could change at any time and our macro would pick it up
  myKey: 'myProp',

  result: filterBy('myArray', 'myKey', 1)
});
curriedComputed

This is a shorthand version of computed. It allows you to create macros like this:

// app/macros/add.js
import curriedComputed from 'ember-macro-helpers/curried-computed';

export default curriedComputed(function(value1, value2) {
  // At this point, the keys no long matter.
  // You are provided the resolved values for you to perform your operation.
  return value1 + value2;
});
lazyComputed

This is the lazy resolving version of computed. The difference is instead of being provided the resolved values, you are provided the unresolved keys and a resolving function. This is useful if you want to optimize your macros and have early returns without calculating every key eagerly.

The API differs only slightly from computed:

// app/macros/and.js
import lazyComputed from 'ember-macro-helpers/lazy-computed';

export default function(key1, key2) {
  return lazyComputed(key1, key2, (get, key1, key2) => {
    // Where normally you get the values, now you have to calculate yourself.
    // The second key won't calculate if the first resolved value is falsy.
    return get(key1) && get(key2);
  });
}
lazyCurriedComputed

This is the combination of lazyComputed and curriedComputed.

literal

alias for raw

raw

This allows you to escape string literals to be used in macros.

Normally, a string means it will look up the property on the object context:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';

export default Ember.Component.extend({
  key: 'value',

  result: computed('key', value => {
    console.log(value); // 'value'
    // do something else
  })
});

But if you just want to use the value without making an object property, you can use the raw macro:

import Ember from 'ember';
import computed from 'ember-macro-helpers/computed';
import raw from 'ember-macro-helpers/raw';

export default Ember.Component.extend({
  key: 'value',

  // Even though we are using a string that is the same name as a property on the object,
  // the `raw` macro will ignore the object property and treat the string as a literal.
  result: computed(raw('key'), value => {
    console.log(value); // 'key'
    // do something else
  })
});

The usefulness is more apparent when using complex macros, for example, when using the string split macro from ember-awesome-macros:

import Ember from 'ember';
import raw from 'ember-macro-helpers/raw';
import split from 'ember-awesome-macros/array/split';

export default Ember.Component.extend({
  key: '1, 2, 3',

  result: split('key', raw(', ')) // [1, 2, 3]
});
reads

alias for writable

writable

This is a setting API for read-only macros.

Given the following read-only macro called sum:

import computed from 'ember-macro-helpers/computed';

export default function(key1, key2) {
  return computed(key1, key2, (value1, value2) => {
    return value1 + value2;
  }).readOnly();
}

and its usage:

key1: 1,
key2: 2,
result: sum('key1', 'key2')

If you try and set result, you will get a read-only exception.

If you want to bring back the setting functionality, you can wrap it in the writable macro:

key1: 1,
key2: 2,
result: writable(sum('key1', 'key2'))

Now, setting result will remove the macro and replace it with your value.

If you want to do something unique when setting, you can provide a set callback:

key1: 1,
key2: 2,
result: writable(sum('key1', 'key2'), {
  set() {
    // do something
    return 'new value';
  }
}), // setting this will not overwrite your macro

or:

key1: 1,
key2: 2,
result: writable(sum('key1', 'key2'), function() {
  // do something
  return 'new value';
}) // same as above, but shorthand

Setting result here will not remove your macro, but will update result with the return value.

Custom macros

The addon provides a way of creating your own custom macros. The easiest way is to use the blueprint generator:

ember generate macro my-custom-macro

This will generate an example macro and its associated test. The comments in these files will get you started.

More explanation is given in the introduction video.

About

Ember macro helpers for making your own fancy macros!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 97.7%
  • HTML 2.3%