diff --git a/addon/index.js b/addon/index.js index 027f4c7..dffd2b5 100644 --- a/addon/index.js +++ b/addon/index.js @@ -1,4 +1,5 @@ import { decorator } from '@ember-decorators/utils/decorator-wrappers'; +import extractValue from '@ember-decorators/utils/extract-value'; import { run } from '@ember/runloop'; function decoratorWithReturnValue(fn) { @@ -28,4 +29,30 @@ function decoratorWithReturnValue(fn) { * ``` * @function */ -export const next = decoratorWithReturnValue(run.next); \ No newline at end of file +export const next = decoratorWithReturnValue(run.next); + +/** + * Decorator that makes the target function runloop aware and binds + * it to the existing context + * + * ```js + * import Component from '@ember/component'; + * import { bind } from 'ember-decorators/runloop'; + * + * export default class ActionDemoComponent extends Component { + * @bind + * foo() { + * // do something + * } + * } + * ``` + * @function + */ +export const bind = decorator(function(target, key, desc/*, params*/) { + const value = extractValue(desc); + // the value of `this` when the function runs is not the instance but rather the context + // of the initializer function (this === {enumerable: true, configurable: false, writable: true, initializer: ƒ}) + // see this issue: https://github.com/babel/babel/issues/6977 + // https://github.com/tc39/proposal-class-fields/issues/62 + return run.bind(target, value); +}); \ No newline at end of file diff --git a/tests/unit/runloop-test.js b/tests/unit/runloop-test.js index a4a60c9..4d27226 100644 --- a/tests/unit/runloop-test.js +++ b/tests/unit/runloop-test.js @@ -1,4 +1,4 @@ -import { next } from '@ember-decorators/runloop'; +import { next, bind } from '@ember-decorators/runloop'; import { module, test } from 'qunit'; module('computed properties'); @@ -37,4 +37,28 @@ test('"run.next" works with arguments', function(assert) { obj.nextMe('wat'); done(); }, 20); +}); + +test('"run.bind" works with es6 class', function(assert) { + assert.expect(1); + + class Foo { + constructor() { + this.prop = 'foo'; + } + + @bind nextMe = () => { + return this.prop; + }; + } + + class Bar extends Foo { + constructor() { + super(); + this.prop = 'bar'; + } + } + + let obj = new Bar(); + assert.equal(obj.nextMe(), 'foo'); }); \ No newline at end of file