diff --git a/packages/ember-htmlbars/lib/hooks/component.js b/packages/ember-htmlbars/lib/hooks/component.js
index 78662caf531..fda3956001b 100644
--- a/packages/ember-htmlbars/lib/hooks/component.js
+++ b/packages/ember-htmlbars/lib/hooks/component.js
@@ -1,6 +1,6 @@
import ComponentNodeManager from "ember-htmlbars/node-managers/component-node-manager";
-export default function componentHook(renderNode, env, scope, tagName, params, attrs, template, visitor) {
+export default function componentHook(renderNode, env, scope, tagName, params, attrs, templates, visitor) {
var state = renderNode.state;
// Determine if this is an initial render or a re-render
@@ -17,7 +17,7 @@ export default function componentHook(renderNode, env, scope, tagName, params, a
params,
attrs,
parentView,
- template,
+ templates,
parentScope: scope
});
diff --git a/packages/ember-htmlbars/lib/hooks/create-fresh-scope.js b/packages/ember-htmlbars/lib/hooks/create-fresh-scope.js
index 6ff0aacc675..37d18b45030 100644
--- a/packages/ember-htmlbars/lib/hooks/create-fresh-scope.js
+++ b/packages/ember-htmlbars/lib/hooks/create-fresh-scope.js
@@ -1,7 +1,7 @@
export default function createFreshScope() {
return {
self: null,
- block: null,
+ blocks: {},
component: null,
view: null,
attrs: null,
diff --git a/packages/ember-htmlbars/lib/hooks/get-root.js b/packages/ember-htmlbars/lib/hooks/get-root.js
index b271ddb7e8b..452dd24bec4 100644
--- a/packages/ember-htmlbars/lib/hooks/get-root.js
+++ b/packages/ember-htmlbars/lib/hooks/get-root.js
@@ -11,9 +11,9 @@ export default function getRoot(scope, key) {
if (key === 'this') {
return [scope.self];
} else if (key === 'hasBlock') {
- return [!!scope.block];
+ return [!!scope.blocks.default];
} else if (key === 'hasBlockParams') {
- return [!!(scope.block && scope.block.arity)];
+ return [!!(scope.blocks.default && scope.blocks.default.arity)];
} else if (isGlobal(key) && Ember.lookup[key]) {
return [getGlobal(key)];
} else if (scope.locals[key]) {
diff --git a/packages/ember-htmlbars/lib/keywords/component.js b/packages/ember-htmlbars/lib/keywords/component.js
index e384245e3fa..6bc4bffaa22 100644
--- a/packages/ember-htmlbars/lib/keywords/component.js
+++ b/packages/ember-htmlbars/lib/keywords/component.js
@@ -27,5 +27,5 @@ function render(morph, env, scope, params, hash, template, inverse, visitor) {
return;
}
- env.hooks.component(morph, env, scope, componentPath, params, hash, template, visitor);
+ env.hooks.component(morph, env, scope, componentPath, params, hash, { default: template, inverse }, visitor);
}
diff --git a/packages/ember-htmlbars/lib/keywords/input.js b/packages/ember-htmlbars/lib/keywords/input.js
index be32c47e94e..dcf88dbddba 100644
--- a/packages/ember-htmlbars/lib/keywords/input.js
+++ b/packages/ember-htmlbars/lib/keywords/input.js
@@ -13,7 +13,7 @@ export default {
},
render(morph, env, scope, params, hash, template, inverse, visitor) {
- env.hooks.component(morph, env, scope, morph.state.componentName, params, hash, template, visitor);
+ env.hooks.component(morph, env, scope, morph.state.componentName, params, hash, { default: template, inverse }, visitor);
},
rerender(...args) {
diff --git a/packages/ember-htmlbars/lib/keywords/legacy-yield.js b/packages/ember-htmlbars/lib/keywords/legacy-yield.js
index 45008aaef09..6b3825b7665 100644
--- a/packages/ember-htmlbars/lib/keywords/legacy-yield.js
+++ b/packages/ember-htmlbars/lib/keywords/legacy-yield.js
@@ -3,7 +3,7 @@ import ProxyStream from "ember-metal/streams/proxy-stream";
export default function legacyYield(morph, env, _scope, params, hash, template, inverse, visitor) {
let scope = _scope;
- if (scope.block.arity === 0) {
+ if (scope.blocks.default.arity === 0) {
// Typically, the `controller` local is persists through lexical scope.
// However, in this case, the `{{legacy-yield}}` in the legacy each view
// needs to override the controller local for the template it is yielding.
@@ -14,9 +14,9 @@ export default function legacyYield(morph, env, _scope, params, hash, template,
scope.locals.controller = new ProxyStream(hash.controller, "controller");
scope.overrideController = true;
}
- scope.block(env, [], params[0], morph, scope, visitor);
+ scope.blocks.default(env, [], params[0], morph, scope, visitor);
} else {
- scope.block(env, params, undefined, morph, scope, visitor);
+ scope.blocks.default(env, params, undefined, morph, scope, visitor);
}
return true;
diff --git a/packages/ember-htmlbars/lib/keywords/textarea.js b/packages/ember-htmlbars/lib/keywords/textarea.js
index 656096ae2b2..daf57bcc087 100644
--- a/packages/ember-htmlbars/lib/keywords/textarea.js
+++ b/packages/ember-htmlbars/lib/keywords/textarea.js
@@ -4,6 +4,6 @@
*/
export default function textarea(morph, env, scope, originalParams, hash, template, inverse, visitor) {
- env.hooks.component(morph, env, scope, '-text-area', originalParams, hash, template, visitor);
+ env.hooks.component(morph, env, scope, '-text-area', originalParams, hash, { default: template, inverse }, visitor);
return true;
}
diff --git a/packages/ember-htmlbars/lib/node-managers/component-node-manager.js b/packages/ember-htmlbars/lib/node-managers/component-node-manager.js
index a6e97a5598b..6db4516b26b 100644
--- a/packages/ember-htmlbars/lib/node-managers/component-node-manager.js
+++ b/packages/ember-htmlbars/lib/node-managers/component-node-manager.js
@@ -32,7 +32,7 @@ ComponentNodeManager.create = function(renderNode, env, options) {
attrs,
parentView,
parentScope,
- template } = options;
+ templates } = options;
attrs = attrs || {};
@@ -79,9 +79,9 @@ ComponentNodeManager.create = function(renderNode, env, options) {
// There is no block template provided but the component has a
// `template` property.
- if (!template && componentTemplate) {
+ if ((!templates || !templates.default) && componentTemplate) {
Ember.deprecate("Using deprecated `template` property on a Component.");
- template = componentTemplate.raw;
+ templates = { default: componentTemplate.raw };
}
} else if (componentTemplate) {
// If the component has a `template` but no `layout`, use the template
@@ -104,7 +104,7 @@ ComponentNodeManager.create = function(renderNode, env, options) {
}
var results = buildComponentTemplate({ layout: layout, component: component }, attrs, {
- template: template,
+ templates,
scope: parentScope
});
diff --git a/packages/ember-htmlbars/lib/system/component-node.js b/packages/ember-htmlbars/lib/system/component-node.js
index 4981934901e..2bec75eddcd 100644
--- a/packages/ember-htmlbars/lib/system/component-node.js
+++ b/packages/ember-htmlbars/lib/system/component-node.js
@@ -79,7 +79,7 @@ ComponentNode.create = function(renderNode, env, attrs, found, parentView, path,
Ember.assert("BUG: ComponentNode.create can take a scope or a self, but not both", !(contentScope && found.self));
var results = buildComponentTemplate(componentInfo, attrs, {
- template: contentTemplate,
+ templates: { default: contentTemplate },
scope: contentScope,
self: found.self
});
diff --git a/packages/ember-htmlbars/tests/integration/component_invocation_test.js b/packages/ember-htmlbars/tests/integration/component_invocation_test.js
index 6db127e635b..2036bd3c282 100644
--- a/packages/ember-htmlbars/tests/integration/component_invocation_test.js
+++ b/packages/ember-htmlbars/tests/integration/component_invocation_test.js
@@ -303,3 +303,88 @@ if (Ember.FEATURES.isEnabled('ember-htmlbars-component-helper')) {
equal(jQuery('#qunit-fixture').text(), 'Edward5');
});
}
+
+QUnit.test('yield to inverse', function() {
+ registry.register('template:components/my-if', compile('{{#if predicate}}Yes:{{yield someValue}}{{else}}No:{{yield to="inverse"}}{{/if}}'));
+
+ view = EmberView.extend({
+ layout: compile('{{#my-if predicate=activated someValue=42 as |result|}}Hello{{result}}{{else}}Goodbye{{/my-if}}'),
+ container: container,
+ context: {
+ activated: true
+ }
+ }).create();
+
+ runAppend(view);
+ equal(jQuery('#qunit-fixture').text(), 'Yes:Hello42');
+ run(function() {
+ Ember.set(view.context, 'activated', false);
+ });
+
+ equal(jQuery('#qunit-fixture').text(), 'No:Goodbye');
+});
+
+QUnit.test('parameterized hasBlock inverse', function() {
+ registry.register('template:components/check-inverse', compile('{{#if (hasBlock "inverse")}}Yes{{else}}No{{/if}}'));
+
+ view = EmberView.extend({
+ layout: compile('{{#check-inverse id="expect-no"}}{{/check-inverse}} {{#check-inverse id="expect-yes"}}{{else}}{{/check-inverse}}'),
+ container: container
+ }).create();
+
+ runAppend(view);
+ equal(jQuery('#qunit-fixture #expect-no').text(), 'No');
+ equal(jQuery('#qunit-fixture #expect-yes').text(), 'Yes');
+});
+
+QUnit.test('parameterized hasBlock default', function() {
+ registry.register('template:components/check-block', compile('{{#if (hasBlock)}}Yes{{else}}No{{/if}}'));
+
+ view = EmberView.extend({
+ layout: compile('{{check-block id="expect-no"}} {{#check-block id="expect-yes"}}{{/check-block}}'),
+ container: container
+ }).create();
+
+ runAppend(view);
+ equal(jQuery('#qunit-fixture #expect-no').text(), 'No');
+ equal(jQuery('#qunit-fixture #expect-yes').text(), 'Yes');
+});
+
+QUnit.test('non-expression hasBlock ', function() {
+ registry.register('template:components/check-block', compile('{{#if hasBlock}}Yes{{else}}No{{/if}}'));
+
+ view = EmberView.extend({
+ layout: compile('{{check-block id="expect-no"}} {{#check-block id="expect-yes"}}{{/check-block}}'),
+ container: container
+ }).create();
+
+ runAppend(view);
+ equal(jQuery('#qunit-fixture #expect-no').text(), 'No');
+ equal(jQuery('#qunit-fixture #expect-yes').text(), 'Yes');
+});
+
+QUnit.test('parameterized hasBlockParams', function() {
+ registry.register('template:components/check-params', compile('{{#if (hasBlockParams)}}Yes{{else}}No{{/if}}'));
+
+ view = EmberView.extend({
+ layout: compile('{{#check-params id="expect-no"}}{{/check-params}} {{#check-params id="expect-yes" as |foo|}}{{/check-params}}'),
+ container: container
+ }).create();
+
+ runAppend(view);
+ equal(jQuery('#qunit-fixture #expect-no').text(), 'No');
+ equal(jQuery('#qunit-fixture #expect-yes').text(), 'Yes');
+});
+
+QUnit.test('non-expression hasBlockParams', function() {
+ registry.register('template:components/check-params', compile('{{#if hasBlockParams}}Yes{{else}}No{{/if}}'));
+
+ view = EmberView.extend({
+ layout: compile('{{#check-params id="expect-no"}}{{/check-params}} {{#check-params id="expect-yes" as |foo|}}{{/check-params}}'),
+ container: container
+ }).create();
+
+ runAppend(view);
+ equal(jQuery('#qunit-fixture #expect-no').text(), 'No');
+ equal(jQuery('#qunit-fixture #expect-yes').text(), 'Yes');
+});
diff --git a/packages/ember-metal-views/lib/renderer.js b/packages/ember-metal-views/lib/renderer.js
index 90374e26a54..a7a93f2370a 100755
--- a/packages/ember-metal-views/lib/renderer.js
+++ b/packages/ember-metal-views/lib/renderer.js
@@ -28,7 +28,7 @@ Renderer.prototype.prerenderTopLevelView =
var block = buildComponentTemplate(componentInfo, {}, {
self: view,
- template: template && template.raw
+ templates: template ? { default: template.raw } : undefined
}).block;
view.renderBlock(block, renderNode);
diff --git a/packages/ember-routing-htmlbars/lib/keywords/link-to.js b/packages/ember-routing-htmlbars/lib/keywords/link-to.js
index 3c456822cb7..6e77f009619 100644
--- a/packages/ember-routing-htmlbars/lib/keywords/link-to.js
+++ b/packages/ember-routing-htmlbars/lib/keywords/link-to.js
@@ -297,7 +297,7 @@ export default {
attrs.escaped = !morph.parseTextAsHTML;
- env.hooks.component(morph, env, scope, '-link-to', params, attrs, template, visitor);
+ env.hooks.component(morph, env, scope, '-link-to', params, attrs, { default: template }, visitor);
},
rerender(morph, env, scope, params, hash, template, inverse, visitor) {
diff --git a/packages/ember-views/lib/system/build-component-template.js b/packages/ember-views/lib/system/build-component-template.js
index 4d2f2af8ac4..3570c7c6ee1 100644
--- a/packages/ember-views/lib/system/build-component-template.js
+++ b/packages/ember-views/lib/system/build-component-template.js
@@ -10,12 +10,11 @@ export default function buildComponentTemplate({ component, layout }, attrs, con
component = null;
}
- if (content.template) {
- blockToRender = createContentBlock(content.template, content.scope, content.self, component);
- }
-
if (layout && layout.raw) {
- blockToRender = createLayoutBlock(layout.raw, blockToRender, content.self, component, attrs);
+ let yieldTo = createContentBlocks(content.templates, content.scope, content.self, component);
+ blockToRender = createLayoutBlock(layout.raw, yieldTo, content.self, component, attrs);
+ } else if (content.templates && content.templates.default) {
+ blockToRender = createContentBlock(content.templates.default, content.scope, content.self, component);
}
if (component) {
@@ -56,9 +55,25 @@ function createContentBlock(template, scope, self, component) {
});
}
+function createContentBlocks(templates, scope, self, component) {
+ if (!templates) {
+ return;
+ }
+ var output = {};
+ for (var name in templates) {
+ if (templates.hasOwnProperty(name)) {
+ var template = templates[name];
+ if (template) {
+ output[name] = createContentBlock(templates[name], scope, self, component);
+ }
+ }
+ }
+ return output;
+}
+
function createLayoutBlock(template, yieldTo, self, component, attrs) {
return blockFor(template, {
- yieldTo: yieldTo,
+ yieldTo,
// If we have an old-style Controller with a template it will be
// passed as our `self` argument, and it should be the context for