diff --git a/packages/ember-htmlbars/lib/hooks/bind-self.js b/packages/ember-htmlbars/lib/hooks/bind-self.js
index ffac5e04cc7..3e7e48da582 100644
--- a/packages/ember-htmlbars/lib/hooks/bind-self.js
+++ b/packages/ember-htmlbars/lib/hooks/bind-self.js
@@ -4,7 +4,7 @@
*/
import SimpleStream from "ember-metal/streams/simple";
-import { componentSymbol } from "ember-htmlbars/hooks/component";
+import { componentSymbol } from "ember-htmlbars/system/component-node";
export default function bindSelf(scope, self) {
var component = self[componentSymbol];
diff --git a/packages/ember-htmlbars/lib/hooks/component.js b/packages/ember-htmlbars/lib/hooks/component.js
index 3a92b1bb202..bc563a55b11 100644
--- a/packages/ember-htmlbars/lib/hooks/component.js
+++ b/packages/ember-htmlbars/lib/hooks/component.js
@@ -1,28 +1,5 @@
-import { get } from "ember-metal/property_get";
-import Ember from "ember-metal/core";
-import { hooks as htmlbarsHooks } from "htmlbars-runtime";
-import { validateChildMorphs } from "htmlbars-util";
-import { readHash } from "ember-metal/streams/utils";
-import { symbol } from "ember-metal/utils";
-import merge from "ember-metal/merge";
-
-export var componentClassSymbol = symbol("componentClass");
-export var componentLayoutSymbol = symbol("componentLayout");
-export var componentSymbol = symbol("component");
-
-// my-button template
-//
-//
-// app template
-// world
-//
-//
-// component
-// world (app-template scope)
-//
-// shadow root (scope={ attrs: { title: "Hello" } })
-// {{attrs.title}}
-// {{yield}}
+import ComponentNode from "ember-htmlbars/system/component-node";
+import { componentClassSymbol, componentLayoutSymbol } from "ember-htmlbars/system/component-node";
export default function componentHook(renderNode, env, scope, tagName, attrs, template, visitor) {
var state = renderNode.state;
@@ -51,111 +28,3 @@ export default function componentHook(renderNode, env, scope, tagName, attrs, te
componentNode.render(env, attrs, visitor, parentView._state === 'inDOM');
}
-function ComponentNode(component, shadowRoot) {
- this.component = component;
- this.shadowRoot = shadowRoot;
-}
-
-ComponentNode.create = function(renderNode, env, found, parentView, tagName, contentScope, contentTemplate) {
- found = found || lookupComponent(env, tagName);
- Ember.assert("Could not find component '" + tagName + "' (no component or template with that name was found)", !!found.component || !!found.layout);
-
- var component, layoutMorph, layoutTemplate;
-
- if (found.component) {
- component = createComponent(found.component, parentView, renderNode);
- component.renderNode = renderNode;
- layoutMorph = component.renderer.contentMorphForView(component, renderNode);
- layoutTemplate = get(component, 'layout') || get(component, 'template') || found.layout;
- } else {
- layoutMorph = renderNode;
- layoutTemplate = found.layout;
- }
-
- var shadowRoot = new ShadowRoot(layoutMorph, component, layoutTemplate,
- contentScope, contentTemplate);
-
- return new ComponentNode(component, shadowRoot);
-};
-
-ComponentNode.prototype.render = function(env, attrs, visitor, inDOM) {
- var component = this.component;
-
- if (component) {
- env.renderer.setAttrs(this.component, readHash(attrs));
- }
-
- this.shadowRoot.render(env, attrs, visitor);
-
- if (component) {
- if (inDOM) {
- component.renderer.didInsertElement(component);
- } else {
- // TODO: This should be on ownerNode, not ownerView
- component.ownerView.newlyCreated.push(component);
- }
- }
-};
-
-ComponentNode.prototype.rerender = function(env, attrs, visitor, shouldRerender) {
- var component = this.component;
-
- if (component) {
- var snapshot = readHash(attrs);
-
- if (shouldRerender) {
- env.renderer.updateAttrs(component, snapshot);
- }
-
- env.renderer.willUpdate(component, snapshot);
- }
-
- validateChildMorphs(this.shadowRoot.layoutMorph, visitor);
-};
-
-function ShadowRoot(layoutMorph, component, layoutTemplate, contentScope, contentTemplate) {
- this.layoutMorph = layoutMorph;
- this.layoutTemplate = layoutTemplate;
- this.hostComponent = component;
-
- this.contentScope = contentScope;
- this.contentTemplate = contentTemplate;
-}
-
-ShadowRoot.prototype.render = function(env, attrs, visitor) {
- if (!this.layoutTemplate) { return; }
-
- var self = { attrs: attrs };
- self[componentSymbol] = this.hostComponent || true;
-
- var hash = { self: self, layout: this.layoutTemplate };
-
- var newEnv = env;
- if (this.hostComponent) {
- newEnv = merge({}, env);
- newEnv.view = this.hostComponent;
- }
-
- // Invoke the `@view` helper. Tell it to render the layout template into the
- // layout morph. When the layout template `{{yield}}`s, it should render the
- // contentTemplate with the contentScope.
- htmlbarsHooks.block(this.layoutMorph, newEnv, this.contentScope, '@view',
- [], hash, this.contentTemplate, null, visitor);
-};
-
-function createComponent(foundComponent, parentView, morph) {
- var component = foundComponent.create();
- parentView.linkChild(component);
- morph.state.view = component;
- return component;
-}
-
-function lookupComponent(env, tagName) {
- var container = env.container;
- var componentLookup = container.lookup('component-lookup:main');
-
- return {
- component: componentLookup.componentFor(tagName, container),
- layout: componentLookup.layoutFor(tagName, container)
- };
-}
diff --git a/packages/ember-htmlbars/lib/keywords/view.js b/packages/ember-htmlbars/lib/keywords/view.js
index e87fa007540..cc2f2cb3fa3 100644
--- a/packages/ember-htmlbars/lib/keywords/view.js
+++ b/packages/ember-htmlbars/lib/keywords/view.js
@@ -3,43 +3,46 @@
@submodule ember-htmlbars
*/
-import { get } from "ember-metal/property_get";
import { readViewFactory } from "ember-views/streams/utils";
-import Ember from "ember-metal/core";
import EmberView from "ember-views/views/view";
+import ComponentNode from "ember-htmlbars/system/component-node";
-export default function viewKeyword(morph, env, scope, params, hash, template, inverse) {
- var read = env.hooks.getValue;
- var parentView = read(scope.locals.view);
- var view = hash.view = getView(read(params[0]), parentView.container);
- parentView.linkChild(view);
+export default {
+ setupState: function(state, env, scope, params, hash) {
+ var read = env.hooks.getValue;
+ state.parentView = read(scope.locals.view);
- morph.state.view = view;
+ debugger;
+ state.lastViewClassOrInstance = state.viewClassOrInstance;
+ state.viewClassOrInstance = getView(read(params[0]), env.container);
+ },
- Ember.assert("Expected morph to have only a single node", morph.firstNode === morph.lastNode);
+ isStable: function(state, env, scope, params, hash) {
+ return state.lastViewClassOrInstance === state.viewClassOrInstance;
+ },
- var dom = env.dom;
+ render: function(node, env, scope, params, hash, template, inverse, visitor) {
+ var state = node.state;
+ var parentView = state.parentView;
- var contentMorph = view.renderer.contentMorphForView(view, morph, dom);
+ var view = hash.view = viewInstance(node.state.viewClassOrInstance);
+ parentView.linkChild(view);
- var viewHasTemplate = get(view, 'template') || get(view, 'layout') || template;
- var inDOM = parentView._state === 'inDOM';
+ state.view = view;
- if (viewHasTemplate) {
- var viewHash = { self: view, layout: get(view, 'template') };
- env.hooks.block(contentMorph, env, scope, '@view', params, viewHash, template, null);
- }
+ var options = { component: view, layout: null };
+ var componentNode = ComponentNode.create(node, env, options, parentView, null, scope, template);
- view.renderer.didCreateElement(view);
+ componentNode.render(env, hash, visitor, parentView._state === 'inDOM');
+ }
+};
- if (inDOM) {
- // TODO: Make sure this gets called once all descendents are also in DOM
- view.renderer.didInsertElement(view);
+function viewInstance(viewClassOrInstance) {
+ if (viewClassOrInstance instanceof EmberView) {
+ return viewClassOrInstance;
} else {
- view.ownerView.newlyCreated.push(view);
+ return viewClassOrInstance.create();
}
-
- return true;
}
function getView(viewPath, container) {
@@ -55,9 +58,5 @@ function getView(viewPath, container) {
viewClassOrInstance = readViewFactory(viewPath, container);
}
- if (viewClassOrInstance instanceof EmberView) {
- return viewClassOrInstance;
- } else {
- return viewClassOrInstance.create();
- }
+ return viewClassOrInstance;
}
diff --git a/packages/ember-htmlbars/lib/system/component-node.js b/packages/ember-htmlbars/lib/system/component-node.js
new file mode 100644
index 00000000000..2ea4a838c05
--- /dev/null
+++ b/packages/ember-htmlbars/lib/system/component-node.js
@@ -0,0 +1,126 @@
+import { get } from "ember-metal/property_get";
+import Ember from "ember-metal/core";
+import { hooks as htmlbarsHooks } from "htmlbars-runtime";
+import { validateChildMorphs } from "htmlbars-util";
+import { readHash } from "ember-metal/streams/utils";
+
+import merge from "ember-metal/merge";
+import { symbol } from "ember-metal/utils";
+
+export var componentClassSymbol = symbol("componentClass");
+export var componentLayoutSymbol = symbol("componentLayout");
+export var componentSymbol = symbol("component");
+
+function ComponentNode(component, shadowRoot) {
+ this.component = component;
+ this.shadowRoot = shadowRoot;
+}
+
+export default ComponentNode;
+
+ComponentNode.create = function(renderNode, env, found, parentView, tagName, contentScope, contentTemplate) {
+ found = found || lookupComponent(env, tagName);
+ Ember.assert("Could not find component '" + tagName + "' (no component or template with that name was found)", !!found.component || !!found.layout);
+
+ var component, layoutMorph, layoutTemplate;
+
+ if (found.component) {
+ component = createComponent(found.component, parentView, renderNode);
+ component.renderNode = renderNode;
+ layoutMorph = component.renderer.contentMorphForView(component, renderNode);
+ layoutTemplate = get(component, 'layout') || get(component, 'template') || found.layout;
+ } else {
+ layoutMorph = renderNode;
+ layoutTemplate = found.layout;
+ }
+
+ var shadowRoot = new ShadowRoot(layoutMorph, component, layoutTemplate,
+ contentScope, contentTemplate);
+
+ return new ComponentNode(component, shadowRoot);
+};
+
+ComponentNode.prototype.render = function(env, attrs, visitor, inDOM) {
+ var component = this.component;
+
+ if (component) {
+ env.renderer.setAttrs(this.component, readHash(attrs));
+ }
+
+ this.shadowRoot.render(env, attrs, visitor);
+
+ if (component) {
+ if (inDOM) {
+ component.renderer.didInsertElement(component);
+ } else {
+ // TODO: This should be on ownerNode, not ownerView
+ component.ownerView.newlyCreated.push(component);
+ }
+ }
+};
+
+ComponentNode.prototype.rerender = function(env, attrs, visitor, shouldRerender) {
+ var component = this.component;
+
+ if (component) {
+ var snapshot = readHash(attrs);
+
+ if (shouldRerender) {
+ env.renderer.updateAttrs(component, snapshot);
+ }
+
+ env.renderer.willUpdate(component, snapshot);
+ }
+
+ validateChildMorphs(this.shadowRoot.layoutMorph, visitor);
+};
+
+function ShadowRoot(layoutMorph, component, layoutTemplate, contentScope, contentTemplate) {
+ this.layoutMorph = layoutMorph;
+ this.layoutTemplate = layoutTemplate;
+ this.hostComponent = component;
+
+ this.contentScope = contentScope;
+ this.contentTemplate = contentTemplate;
+}
+
+ShadowRoot.prototype.render = function(env, attrs, visitor) {
+ if (!this.layoutTemplate && !this.contentTemplate) { return; }
+
+ var self = { attrs: attrs };
+ self[componentSymbol] = this.hostComponent || true;
+
+ var hash = { self: self, layout: this.layoutTemplate };
+
+ var newEnv = env;
+ if (this.hostComponent) {
+ newEnv = merge({}, env);
+ newEnv.view = this.hostComponent;
+ }
+
+ // Invoke the `@view` helper. Tell it to render the layout template into the
+ // layout morph. When the layout template `{{yield}}`s, it should render the
+ // contentTemplate with the contentScope.
+ htmlbarsHooks.block(this.layoutMorph, newEnv, this.contentScope, '@view',
+ [], hash, this.contentTemplate || null, null, visitor);
+};
+
+function createComponent(component, parentView, morph) {
+ if (component.create) {
+ component = component.create();
+ }
+
+ parentView.linkChild(component);
+ morph.state.view = component;
+ return component;
+}
+
+function lookupComponent(env, tagName) {
+ var container = env.container;
+ var componentLookup = container.lookup('component-lookup:main');
+
+ return {
+ component: componentLookup.componentFor(tagName, container),
+ layout: componentLookup.layoutFor(tagName, container)
+ };
+}