Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,42 @@ for a detailed explanation.
```

Added in [#10160](https://github.com/emberjs/ember.js/pull/10160)

* `ember-htmlbars-scoped-helpers`

Adds the ability for helpers to be resolved from the local component. Allowing components to
use local helpers (that do not get made available globally) AND/OR providing local helpers to
the provided block param.

Block Params Example:

```javascript
// app/components/form-for.js

import Ember from "ember";
import FormForInput from "./helpers/input";

var makeViewHelper = Ember.HTMLBars.makeViewHelper;

export default Ember.Component.extend({
formHelpers: {
input: makeViewHelper(FormForInput)
}
});
```

```handlebars
{{! app/templates/components/form-for.hbs }}

{{yield formHelpers}}
```

```handlebars
{{! app/templates/post/new.hbs }}

{{#form-for as |f|}}
{{f.input label="Title" value=model.title}}
{{/form-for}}
```

Added in [#10244](https://github.com/emberjs/ember.js/pull/10244)
3 changes: 2 additions & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"ember-testing-checkbox-helpers": null,
"ember-metal-stream": null,
"ember-htmlbars-each-with-index": true,
"ember-application-initializer-context": null
"ember-application-initializer-context": null,
"ember-htmlbars-scoped-helpers": null
},
"debugStatements": [
"Ember.warn",
Expand Down
7 changes: 7 additions & 0 deletions packages/ember-htmlbars/lib/system/lookup-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export default function lookupHelper(name, view, env) {
return helper;
}

if (Ember.FEATURES.isEnabled('ember-htmlbars-scoped-helpers')) {
helper = view.getStream(name).value();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a :trollface: to ask if this should be bound?

if (helper && helper.isHTMLBars) {
return helper;
}
}

var container = view.container;

if (!container || ISNT_HELPER_CACHE.get(name)) {
Expand Down
97 changes: 97 additions & 0 deletions packages/ember-htmlbars/tests/integration/helper_lookup_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Registry from "container/registry";
import run from "ember-metal/run_loop";
import ComponentLookup from 'ember-views/component_lookup';
import View from "ember-views/views/view";
import Component from "ember-views/views/component";
import compile from "ember-template-compiler/system/compile";
import makeBoundHelper from "ember-htmlbars/system/make_bound_helper";
import makeViewHelper from "ember-htmlbars/system/make-view-helper";
import { runAppend, runDestroy } from "ember-runtime/tests/utils";

var registry, container, view;

var blockParamsEnabled;
if (Ember.FEATURES.isEnabled('ember-htmlbars-block-params')) {
blockParamsEnabled = true;
}

if (Ember.FEATURES.isEnabled('ember-htmlbars-scoped-helpers')) {
// jscs:disable validateIndentation

QUnit.module("ember-htmlbars: helper lookup -- scoped helpers", {
setup: function() {
registry = new Registry();
container = registry.container();
registry.optionsForType('component', { singleton: false });
registry.optionsForType('view', { singleton: false });
registry.optionsForType('template', { instantiate: false });
registry.optionsForType('helper', { instantiate: false });
registry.register('component-lookup:main', ComponentLookup);
},

teardown: function() {
runDestroy(container);
runDestroy(view);

registry = container = view = null;
}
});

test("basic scoped helper usage", function() {
view = View.create({
name: 'lucy',

capitalize: makeBoundHelper(function(params) {
return params[0].toUpperCase();
}),

template: compile('{{view.name}} - {{view.capitalize view.name}}')
});

runAppend(view);

equal(view.$().text(), "lucy - LUCY");

run(function() {
view.set('name', "molly");
});

equal(view.$().text(), "molly - MOLLY");
});

if (blockParamsEnabled) {
test("scoped helper usage through block param", function() {
registry.register('template:components/form-for', compile('{{yield formHelpers}}'));

registry.register('component:form-for', Component.extend({
tagName: 'form',

formHelpers: {
input: makeViewHelper(Component.extend({
layout: compile('<label {{bind-attr for=inputId.elementId}}>{{label}}</label>{{input value=value viewName="inputId"}}')
}))
}
}));

view = View.create({
firstName: 'Robert',
container: container,
template: compile('{{#form-for as |f|}} {{f.input label="First Name" value=view.firstName}} {{/form-for}}')
});

runAppend(view);

equal(view.$('form').length, 1, 'form was created');
equal(view.$('label').text(), 'First Name', 'label was created');
equal(view.$('input').val(), 'Robert', 'input was created');

run(function() {
view.set('firstName', 'Jacquie');
});

equal(view.$('input').val(), 'Jacquie', 'input was updated');
});
}

// jscs:enable validateIndentation
}
65 changes: 54 additions & 11 deletions packages/ember-htmlbars/tests/system/lookup-helper_test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import lookupHelper from "ember-htmlbars/system/lookup-helper";
import ComponentLookup from "ember-views/component_lookup";
import EmberComponent from "ember-views/views/component";
import Registry from "container/registry";
import Component from "ember-views/views/component";

Expand Down Expand Up @@ -33,7 +34,7 @@ test('looks for helpers in the provided `env.helpers`', function() {

test('returns undefined if no container exists (and helper is not found in env)', function() {
var env = generateEnv();
var view = {};
var view = EmberComponent.create();

var actual = lookupHelper('flubarb', view, env);

Expand All @@ -42,13 +43,13 @@ test('returns undefined if no container exists (and helper is not found in env)'

test('does not lookup in the container if the name does not contain a dash (and helper is not found in env)', function() {
var env = generateEnv();
var view = {
var view = EmberComponent.create({
container: {
lookup: function() {
ok(false, 'should not lookup in the container');
}
}
};
});

var actual = lookupHelper('flubarb', view, env);

Expand All @@ -57,9 +58,9 @@ test('does not lookup in the container if the name does not contain a dash (and

test('does a lookup in the container if the name contains a dash (and helper is not found in env)', function() {
var env = generateEnv();
var view = {
var view = EmberComponent.create({
container: generateContainer()
};
});

function someName() {}
someName.isHTMLBars = true;
Expand All @@ -73,9 +74,9 @@ test('does a lookup in the container if the name contains a dash (and helper is
test('wraps helper from container in a Handlebars compat helper', function() {
expect(2);
var env = generateEnv();
var view = {
var view = EmberComponent.create({
container: generateContainer()
};
});
var called;

function someName() {
Expand All @@ -100,9 +101,9 @@ test('wraps helper from container in a Handlebars compat helper', function() {

test('asserts if component-lookup:main cannot be found', function() {
var env = generateEnv();
var view = {
var view = EmberComponent.create({
container: generateContainer()
};
});

view.container._registry.unregister('component-lookup:main');

Expand All @@ -113,13 +114,55 @@ test('asserts if component-lookup:main cannot be found', function() {

test('registers a helper in the container if component is found', function() {
var env = generateEnv();
var view = {
var view = EmberComponent.create({
container: generateContainer()
};
});

view.container._registry.register('component:some-name', Component);

lookupHelper('some-name', view, env);

ok(view.container.lookup('helper:some-name'), 'new helper was registered');
});

if (Ember.FEATURES.isEnabled('ember-htmlbars-scoped-helpers')) {
test('local helpers do not override global helpers', function() {
var env = generateEnv({
'flubarb': function() { }
});

var view = EmberComponent.create({
flubarb: {
isHTMLBars: true
}
});

var actual = lookupHelper('flubarb', view, env);

equal(actual, env.helpers.flubarb, 'helper from env wins over view local helpers');
});

test('looks helpers up on the view for non-global helpers', function() {
var env = generateEnv();
var view = EmberComponent.create({
flubarb: {
isHTMLBars: true
}
});

var actual = lookupHelper('flubarb', view, env);

equal(actual, view.flubarb, 'matches helpers in the root of the view');
});

test('does not match local helpers/props without isHTMLBars: true', function() {
var env = generateEnv();
var view = EmberComponent.create({
flubarb: { }
});

var actual = lookupHelper('flubarb', view, env);

equal(actual, undefined, 'does not match local helpers without isHTMLBars: true');
});
}