Natural Performance Manager

    ember-macro-helpers

    4.2.2 • Public • Published

    ember-macro-helpers

    npm version Build Status

    Ember macro helpers for making your own fancy macros!

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

    Compatibility

    • Ember.js v3.4 or above
    • Ember CLI v2.13 or above
    • Node.js v8 or above

    Installation

    ember install ember-macro-helpers
    

    Usage

    import nameOfMacro from 'ember-macro-helpers/name-of-macro';
    // or
    import { nameOfMacro } from 'ember-macro-helpers';
    

    Contents

    API

    computed

    computed behaves like Ember.computed with some extra benefits.

    It will pass you resolved values:

    import Component from '@ember/component';
    import computed from 'ember-macro-helpers/computed';
     
    export default 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 Component from '@ember/component';
    import { or } from '@ember/object/computed';
    import computed from 'ember-macro-helpers/computed';
     
    export default Component.extend({
      key1: false,
      key2: true,
     
      result: 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 Component from '@ember/component';
    import computed from 'ember-macro-helpers/computed';
    import { conditional, gt, sum, difference } from 'ember-awesome-macros';
     
    export default 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 Component from '@ember/component';
    import computed from 'ember-macro-helpers/computed';
     
    export default Component.extend({
      key1: [{ key2: 1 }, { key2: 2 }],
     
      computed1: computed('key1.[]', value => {
        console.log(value); // [{ key2: 1 }, { key2: 2 }]
        // do something else
      }),
      computed2: computed('key1.@each.key2', value => {
        console.log(value); // [{ key2: 1 }, { key2: 2 }]
        // do something else
      }),
    });

    It resolves property expansion for you:

    import Component from '@ember/component';
    import computed from 'ember-macro-helpers/computed';
     
    export default 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 Component from '@ember/component';
    import add from 'my-app/macros/add';
     
    export default 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,
     
        // any static properties after the last dynamic property are optional
        // you could leave this off if you want
        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 Component from '@ember/component';
    import { A as emberA } from '@ember/array';
    import EmberObject from '@ember/object';
    import filterBy from 'my-app/macros/filter-by';
     
    export default Component.extend({
      myArray: emberA([
        EmberObject.create({ myProp: 0 }),
        EmberObject.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 Component from '@ember/component';
    import computed from 'ember-macro-helpers/computed';
     
    export default 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 Component from '@ember/component';
    import computed from 'ember-macro-helpers/computed';
    import raw from 'ember-macro-helpers/raw';
     
    export default 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 Component from '@ember/component';
    import raw from 'ember-macro-helpers/raw';
    import split from 'ember-awesome-macros/array/split';
     
    export default 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.

    Test helpers

    This comes with a compute helper. Here is a sample usage:

    import myMacro from 'my-app/macros/my-macro';
    import compute from 'ember-macro-helpers/test-support';
     
    // ...
     
    test('it works', function(assert) {
      compute({
        assert,
        computed: myMacro('key1', 'key2'),
        properties: {
          key1: 1,
          key2: 2
        },
        strictEqual: 3
      });
    });

    View all the possible ways to use here.

    Contributing

    See the Contributing guide for details.

    License

    This project is licensed under the MIT License.

    Install

    npm i ember-macro-helpers

    DownloadsWeekly Downloads

    50,327

    Version

    4.2.2

    License

    MIT

    Unpacked Size

    78.2 kB

    Total Files

    38

    Last publish

    Collaborators

    • avatar