From 6368254d0dc4ee6c11a71f04f17086ae67b5e089 Mon Sep 17 00:00:00 2001 From: Matthew Beale Date: Sat, 18 Apr 2015 15:34:42 -0400 Subject: [PATCH] Completely remove the concept of isVirtual MetamorphView (and the Metamorph mixin) are the final bastion of virtual views. This commit removes much of their use. MetamorphView had two behaviors: First, it was tagless. Second, it was not included in the view hierarchy. The first concept (taglessness) is still valid in Ember post-Glimmer. For this, we use `tagName: ''`. The second concept (virtual views) is not longer present. So views which previously used MetamorphView will not longer be able to escape the view hierarchy. In most cases, the intent was likely to be tagless and being virtual was simply a side effect. OutletView and EachView will now be present in childViews and will be parentViews. `{{render` used to always create a view. Now, it will only create a view if one is set by the user. By default it is only a componentNode and has no view at all. The final spot MetamorphViews are used it for the default itemView and emptyView on EachView. This should be easy to address in a manner similar to {{render: If the user does not specify a view, none should be required. After this, MetamorphView will no longer be used by internals. --- .../lib/system/application.js | 2 - .../lib/system/component-node.js | 4 +- .../tests/compat/handlebars_get_test.js | 2 - .../tests/helpers/bind_attr_test.js | 2 - .../tests/helpers/collection_test.js | 1 - .../ember-htmlbars/tests/helpers/each_test.js | 7 - .../tests/helpers/if_unless_test.js | 2 - .../ember-htmlbars/tests/helpers/view_test.js | 2 - .../tests/integration/with_view_test.js | 2 - .../lib/keywords/render.js | 38 +++-- .../tests/helpers/render_test.js | 144 ++++++++++++------ .../ember-routing-htmlbars/tests/utils.js | 2 - .../ember-routing-views/lib/views/outlet.js | 4 +- packages/ember-routing/lib/system/router.js | 20 +-- .../lib/mixins/view_child_views_support.js | 4 +- packages/ember-views/lib/views/core_view.js | 11 -- .../ember-views/lib/views/metamorph_view.js | 1 - .../ember-views/lib/views/states/in_dom.js | 2 +- packages/ember-views/lib/views/view.js | 2 +- .../tests/system/event_dispatcher_test.js | 1 + .../tests/views/metamorph_view_test.js | 51 +++++++ packages/ember/tests/routing/basic_test.js | 4 +- 22 files changed, 196 insertions(+), 112 deletions(-) diff --git a/packages/ember-application/lib/system/application.js b/packages/ember-application/lib/system/application.js index b33ebdee7ee..c1a8b151743 100644 --- a/packages/ember-application/lib/system/application.js +++ b/packages/ember-application/lib/system/application.js @@ -24,7 +24,6 @@ import DOMHelper from "ember-htmlbars/system/dom-helper"; import SelectView from "ember-views/views/select"; import { OutletView } from "ember-routing-views/views/outlet"; import EmberView from "ember-views/views/view"; -import _MetamorphView from "ember-views/views/metamorph_view"; import EventDispatcher from "ember-views/system/event_dispatcher"; import jQuery from "ember-views/system/jquery"; import Route from "ember-routing/system/route"; @@ -1014,7 +1013,6 @@ Application.reopenClass({ registry.injection('view', '_viewRegistry', '-view-registry:main'); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); registry.register('route:basic', Route, { instantiate: false }); diff --git a/packages/ember-htmlbars/lib/system/component-node.js b/packages/ember-htmlbars/lib/system/component-node.js index 6ad704f5e4d..709614ed0df 100644 --- a/packages/ember-htmlbars/lib/system/component-node.js +++ b/packages/ember-htmlbars/lib/system/component-node.js @@ -164,10 +164,8 @@ export function createOrUpdateComponent(component, options, renderNode, env, att if (options.parentView) { options.parentView.appendChild(component); - // don't set the property on a virtual view, as they are invisible to - // consumers of the view API if (options.viewName) { - set(get(options.parentView, 'concreteView'), options.viewName, component); + set(options.parentView, options.viewName, component); } } diff --git a/packages/ember-htmlbars/tests/compat/handlebars_get_test.js b/packages/ember-htmlbars/tests/compat/handlebars_get_test.js index 5ef2e817638..c592db6e459 100644 --- a/packages/ember-htmlbars/tests/compat/handlebars_get_test.js +++ b/packages/ember-htmlbars/tests/compat/handlebars_get_test.js @@ -1,5 +1,4 @@ import Ember from "ember-metal/core"; // Ember.lookup -import _MetamorphView from "ember-views/views/metamorph_view"; import EmberView from "ember-views/views/view"; import handlebarsGet from "ember-htmlbars/compat/handlebars-get"; import { Registry } from "ember-runtime/system/container"; @@ -19,7 +18,6 @@ QUnit.module("ember-htmlbars: compat - Ember.Handlebars.get", { container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.optionsForType('helper', { instantiate: false }); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); }, diff --git a/packages/ember-htmlbars/tests/helpers/bind_attr_test.js b/packages/ember-htmlbars/tests/helpers/bind_attr_test.js index 4224e700159..295276d7e9a 100644 --- a/packages/ember-htmlbars/tests/helpers/bind_attr_test.js +++ b/packages/ember-htmlbars/tests/helpers/bind_attr_test.js @@ -5,7 +5,6 @@ import Ember from "ember-metal/core"; // Ember.lookup import run from "ember-metal/run_loop"; import Namespace from "ember-runtime/system/namespace"; import EmberView from "ember-views/views/view"; -import _MetamorphView from "ember-views/views/metamorph_view"; import EmberObject from "ember-runtime/system/object"; import { A } from "ember-runtime/system/native_array"; import { computed } from "ember-metal/computed"; @@ -35,7 +34,6 @@ QUnit.module("ember-htmlbars: {{bind-attr}} [DEPRECATED]", { registry = new Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); warnings = []; diff --git a/packages/ember-htmlbars/tests/helpers/collection_test.js b/packages/ember-htmlbars/tests/helpers/collection_test.js index 77f626308de..3599514ce1c 100644 --- a/packages/ember-htmlbars/tests/helpers/collection_test.js +++ b/packages/ember-htmlbars/tests/helpers/collection_test.js @@ -41,7 +41,6 @@ QUnit.module("collection helper", { container = registry.container(); registry.optionsForType('template', { instantiate: false }); - // registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); }, diff --git a/packages/ember-htmlbars/tests/helpers/each_test.js b/packages/ember-htmlbars/tests/helpers/each_test.js index 983ad2f72c7..8e383b73bfa 100644 --- a/packages/ember-htmlbars/tests/helpers/each_test.js +++ b/packages/ember-htmlbars/tests/helpers/each_test.js @@ -3,7 +3,6 @@ import Ember from "ember-metal/core"; // Ember.lookup; import EmberObject from "ember-runtime/system/object"; import run from "ember-metal/run_loop"; import EmberView from "ember-views/views/view"; -import _MetamorphView from "ember-views/views/metamorph_view"; import LegacyEachView from "ember-views/views/legacy_each_view"; import { computed } from "ember-metal/computed"; import ArrayController from "ember-runtime/controllers/array_controller"; @@ -88,7 +87,6 @@ QUnit.module("the #each helper [DEPRECATED]", { registry = new Registry(); container = registry.container(); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); registry.register('view:-legacy-each', LegacyEachView); @@ -574,8 +572,6 @@ QUnit.test("it supports {{itemViewClass=}} with tagName (DEPRECATED)", function( container: container }); - //expectDeprecation(/Supplying a tagName to Metamorph views is unreliable and is deprecated./); - runAppend(view); equal(view.$('ul').length, 1, 'rendered ul tag'); equal(view.$('ul li').length, 2, 'rendered 2 li tags'); @@ -681,8 +677,6 @@ QUnit.test("it supports {{emptyViewClass=}} with tagName (DEPRECATED)", function container: container }); - //expectDeprecation(/Supplying a tagName to Metamorph views is unreliable and is deprecated./); - runAppend(view); equal(view.$('b').length, 1, 'rendered b tag'); @@ -796,7 +790,6 @@ function testEachWithItem(moduleName, useBlockParams) { registry = new Registry(); container = registry.container(); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); }, teardown() { diff --git a/packages/ember-htmlbars/tests/helpers/if_unless_test.js b/packages/ember-htmlbars/tests/helpers/if_unless_test.js index a892329737a..9d6c22963b4 100644 --- a/packages/ember-htmlbars/tests/helpers/if_unless_test.js +++ b/packages/ember-htmlbars/tests/helpers/if_unless_test.js @@ -4,7 +4,6 @@ import { Registry } from "ember-runtime/system/container"; import EmberView from "ember-views/views/view"; import ObjectProxy from "ember-runtime/system/object_proxy"; import EmberObject from "ember-runtime/system/object"; -import _MetamorphView from 'ember-views/views/metamorph_view'; import compile from "ember-template-compiler/system/compile"; import { set } from 'ember-metal/property_set'; @@ -24,7 +23,6 @@ QUnit.module("ember-htmlbars: {{#if}} and {{#unless}} helpers", { registry = new Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); }, diff --git a/packages/ember-htmlbars/tests/helpers/view_test.js b/packages/ember-htmlbars/tests/helpers/view_test.js index 08309390254..f68e38305c5 100644 --- a/packages/ember-htmlbars/tests/helpers/view_test.js +++ b/packages/ember-htmlbars/tests/helpers/view_test.js @@ -7,7 +7,6 @@ import TextField from 'ember-views/views/text_field'; import Namespace from 'ember-runtime/system/namespace'; import EmberObject from 'ember-runtime/system/object'; import ContainerView from 'ember-views/views/container_view'; -import _MetamorphView from 'ember-views/views/metamorph_view'; import SafeString from 'htmlbars-util/safe-string'; import precompile from 'ember-template-compiler/compat/precompile'; import compile from "ember-template-compiler/system/compile"; @@ -49,7 +48,6 @@ QUnit.module("ember-htmlbars: {{#view}} helper", { container = registry.container(); registry.optionsForType('template', { instantiate: false }); registry.optionsForType('helper', { instantiate: false }); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); }, diff --git a/packages/ember-htmlbars/tests/integration/with_view_test.js b/packages/ember-htmlbars/tests/integration/with_view_test.js index 075c7b6783e..1fb52013a7b 100644 --- a/packages/ember-htmlbars/tests/integration/with_view_test.js +++ b/packages/ember-htmlbars/tests/integration/with_view_test.js @@ -3,7 +3,6 @@ import jQuery from 'ember-views/system/jquery'; import EmberView from 'ember-views/views/view'; import { Registry } from "ember-runtime/system/container"; import EmberObject from 'ember-runtime/system/object'; -import _MetamorphView from 'ember-views/views/metamorph_view'; import compile from 'ember-template-compiler/system/compile'; import { runAppend, runDestroy } from "ember-runtime/tests/utils"; @@ -17,7 +16,6 @@ QUnit.module('ember-htmlbars: {{#with}} and {{#view}} integration', { registry = new Registry(); container = registry.container(); registry.optionsForType('template', { instantiate: false }); - registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); }, diff --git a/packages/ember-routing-htmlbars/lib/keywords/render.js b/packages/ember-routing-htmlbars/lib/keywords/render.js index c54d6037ee1..af299253719 100644 --- a/packages/ember-routing-htmlbars/lib/keywords/render.js +++ b/packages/ember-routing-htmlbars/lib/keywords/render.js @@ -1,4 +1,5 @@ import Ember from "ember-metal/core"; // assert +import { get } from "ember-metal/property_get"; import EmberError from "ember-metal/error"; import create from 'ember-metal/platform/create'; import { isStream, read } from "ember-metal/streams/utils"; @@ -49,6 +50,12 @@ export default { var context = params[1]; var container = env.container; + + // The render keyword presumes it can work without a router. This is really + // only to satisfy the test: + // + // {{view}} should not override class bindings defined on a child view" + // var router = container.lookup('router:main'); Ember.assert( @@ -61,7 +68,7 @@ export default { Ember.assert( "You can only use the {{render}} helper once without a model object as " + "its second argument, as in {{render \"post\" post}}.", - !router || !router._lookupActiveView(name) + !router || !router._lookupActiveComponentNode(name) ); } else if (params.length !== 2) { throw new EmberError("You must pass a templateName to render"); @@ -81,9 +88,15 @@ export default { var view = container.lookup('view:' + name); if (!view) { view = container.lookup('view:default'); - template = template || container.lookup(templateName); } - view.ownerView = env.view.ownerView; + var viewHasTemplateSpecified = view && !!get(view, 'template'); + if (!template && !viewHasTemplateSpecified) { + template = container.lookup(templateName); + } + + if (view) { + view.ownerView = env.view.ownerView; + } // provide controller override var controllerName; @@ -129,16 +142,13 @@ export default { }); } - view.set('controller', controller); + if (view) { + view.set('controller', controller); + } state.controller = controller; hash.viewName = camelize(name); - if (router && params.length === 1) { - router._connectActiveView(name, view); - } - - // var state = node.state; // var parentView = scope.view; if (template && template.raw) { @@ -146,13 +156,21 @@ export default { } var options = { - component: view, layout: null, self: controller }; + if (view) { + options.component = view; + } + var componentNode = ComponentNode.create(node, env, hash, options, state.parentView, null, null, template); state.componentNode = componentNode; + + if (router && params.length === 1) { + router._connectActiveComponentNode(name, componentNode); + } + componentNode.render(env, hash, visitor); }, diff --git a/packages/ember-routing-htmlbars/tests/helpers/render_test.js b/packages/ember-routing-htmlbars/tests/helpers/render_test.js index 4d19e86fec0..88391f9ccb1 100644 --- a/packages/ember-routing-htmlbars/tests/helpers/render_test.js +++ b/packages/ember-routing-htmlbars/tests/helpers/render_test.js @@ -55,13 +55,16 @@ QUnit.test("{{render}} helper should render given template", function() { runAppend(view); equal(view.$().text(), 'HIBYE'); - ok(container.lookup('router:main')._lookupActiveView('home'), 'should register home as active view'); + // This is a poor assertion. What is really being tested is that + // a second render with the same name will throw an assert. + ok(container.lookup('router:main')._lookupActiveComponentNode('home'), 'should register home as active view'); }); -QUnit.skip("{{render}} helper should render nested helpers", function() { +QUnit.test("{{render}} helper should render nested helpers", function() { var template = "

HI

{{render 'foo'}}"; var controller = EmberController.extend({ container: container }); view = EmberView.create({ + container: container, controller: controller.create(), template: compile(template) }); @@ -140,15 +143,19 @@ QUnit.test("{{render}} helper should render given template with a supplied model template: compile(template) }); - var PostController = EmberController.extend(); + var postController; + var PostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + postController = this; + } + }); container._registry.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile("

{{model.title}}

"); runAppend(view); - var postController = view.childViews[0].get('controller'); - equal(view.$().text(), 'HIRails is omakase'); equal(postController.get('model'), post); @@ -211,21 +218,28 @@ QUnit.test("{{render}} helper should raise an error when a given controller name }); QUnit.test("{{render}} helper should render with given controller", function() { - var template = '

HI

{{render "home" controller="posts"}}'; + var template = '{{render "home" controller="posts"}}'; var controller = EmberController.extend({ container: container }); - container._registry.register('controller:posts', EmberArrayController.extend()); + var id = 0; + container._registry.register('controller:posts', EmberArrayController.extend({ + init() { + this._super.apply(this, arguments); + this.uniqueId = id++; + } + })); view = EmberView.create({ container: container, controller: controller.create(), template: compile(template) }); - Ember.TEMPLATES['home'] = compile("

BYE

"); + Ember.TEMPLATES['home'] = compile("{{uniqueId}}"); runAppend(view); - var renderedView = container.lookup('router:main')._lookupActiveView('home'); - equal(container.lookup('controller:posts'), renderedView.get('controller'), 'rendered with correct controller'); + var uniqueId = container.lookup('controller:posts').get('uniqueId'); + equal(uniqueId, 0, 'precond - first uniqueId is used for singleton'); + equal(uniqueId, view.$().html(), 'rendered with singleton controller'); }); QUnit.test("{{render}} helper should render a template without a model only once", function() { @@ -267,16 +281,23 @@ QUnit.test("{{render}} helper should render templates with models multiple times template: compile(template) }); - var PostController = EmberController.extend(); + var postController1, postController2; + var PostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + if (!postController1) { + postController1 = this; + } else if (!postController2) { + postController2 = this; + } + } + }); container._registry.register('controller:post', PostController, { singleton: false }); Ember.TEMPLATES['post'] = compile("

{{model.title}}

"); runAppend(view); - var postController1 = view.childViews[0].get('controller'); - var postController2 = view.childViews[1].get('controller'); - ok(view.$().text().match(/^HI ?Me first ?Then me$/)); equal(postController1.get('model'), post1); equal(postController2.get('model'), post2); @@ -310,18 +331,22 @@ QUnit.test("{{render}} helper should not leak controllers", function() { template: compile(template) }); - var PostController = EmberController.extend(); + var postController; + var PostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + postController = this; + } + }); container._registry.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile("

{{title}}

"); runAppend(view); - var postController1 = view.childViews[0].get('controller'); - runDestroy(view); - ok(postController1.isDestroyed, 'expected postController to be destroyed'); + ok(postController.isDestroyed, 'expected postController to be destroyed'); }); QUnit.test("{{render}} helper should not treat invocations with falsy contexts as context-less", function() { @@ -336,16 +361,23 @@ QUnit.test("{{render}} helper should not treat invocations with falsy contexts a template: compile(template) }); - var PostController = EmberController.extend(); + var postController1, postController2; + var PostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + if (!postController1) { + postController1 = this; + } else if (!postController2) { + postController2 = this; + } + } + }); container._registry.register('controller:post', PostController, { singleton: false }); Ember.TEMPLATES['post'] = compile("

{{#unless model}}NOTHING{{/unless}}

"); runAppend(view); - var postController1 = view.childViews[0].get('controller'); - var postController2 = view.childViews[1].get('controller'); - ok(view.$().text().match(/^HI ?NOTHING ?NOTHING$/)); equal(postController1.get('model'), 0); equal(postController2.get('model'), undefined); @@ -370,16 +402,23 @@ QUnit.test("{{render}} helper should render templates both with and without mode template: compile(template) }); - var PostController = EmberController.extend(); + var postController1, postController2; + var PostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + if (!postController1) { + postController1 = this; + } else if (!postController2) { + postController2 = this; + } + } + }); container._registry.register('controller:post', PostController, { singleton: false }); Ember.TEMPLATES['post'] = compile("

Title:{{model.title}}

"); runAppend(view); - var postController1 = view.childViews[0].get('controller'); - var postController2 = view.childViews[1].get('controller'); - ok(view.$().text().match(/^HI ?Title: ?Title:Rails is omakase$/)); equal(postController1.get('model'), null); equal(postController2.get('model'), post); @@ -475,45 +514,63 @@ QUnit.skip("{{render}} helper should be able to render a template again when it }); QUnit.test("{{render}} works with dot notation", function() { - var template = '

BLOG

{{render "blog.post"}}'; + var template = '{{render "blog.post"}}'; - var controller = EmberController.extend({ container: container }); - container._registry.register('controller:blog.post', EmberController.extend()); + var ContextController = EmberController.extend({ container: container }); + + var controller; + var id = 0; + var BlogPostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + controller = this; + this.uniqueId = id++; + } + }); + container._registry.register('controller:blog.post', BlogPostController); view = EmberView.create({ container: container, - controller: controller.create(), + controller: ContextController.create(), template: compile(template) }); - Ember.TEMPLATES['blog.post'] = compile("

POST

"); + Ember.TEMPLATES['blog.post'] = compile("{{uniqueId}}"); runAppend(view); - var renderedView = container.lookup('router:main')._lookupActiveView('blog.post'); - equal(renderedView.get('viewName'), 'blogPost', 'camelizes the view name'); - equal(container.lookup('controller:blog.post'), renderedView.get('controller'), 'rendered with correct controller'); + var singletonController = container.lookup('controller:blog.post'); + equal(singletonController.uniqueId, view.$().html(), 'rendered with correct singleton controller'); }); QUnit.test("{{render}} works with slash notation", function() { - var template = '

BLOG

{{render "blog/post"}}'; + var template = '{{render "blog/post"}}'; - var controller = EmberController.extend({ container: container }); - container._registry.register('controller:blog.post', EmberController.extend()); + var ContextController = EmberController.extend({ container: container }); + + var controller; + var id = 0; + var BlogPostController = EmberController.extend({ + init() { + this._super.apply(this, arguments); + controller = this; + this.uniqueId = id++; + } + }); + container._registry.register('controller:blog.post', BlogPostController); view = EmberView.create({ container: container, - controller: controller.create(), + controller: ContextController.create(), template: compile(template) }); - Ember.TEMPLATES['blog.post'] = compile("

POST

"); + Ember.TEMPLATES['blog.post'] = compile("{{uniqueId}}"); runAppend(view); - var renderedView = container.lookup('router:main')._lookupActiveView('blog.post'); - equal(renderedView.get('viewName'), 'blogPost', 'camelizes the view name'); - equal(container.lookup('controller:blog.post'), renderedView.get('controller'), 'rendered with correct controller'); + var singletonController = container.lookup('controller:blog.post'); + equal(singletonController.uniqueId, view.$().html(), 'rendered with correct singleton controller'); }); QUnit.test("throws an assertion if {{render}} is called with an unquoted template name", function() { @@ -569,10 +626,11 @@ QUnit.test("{{render}} helper should let view provide its own template", functio equal(view.$().text(), 'Hello other!'); }); -QUnit.skip("{{render}} helper should not require view to provide its own template", function() { +QUnit.test("{{render}} helper should not require view to provide its own template", function() { var template = "{{render 'fish'}}"; var controller = EmberController.extend({ container: container }); view = EmberView.create({ + container: container, controller: controller.create(), template: compile(template) }); diff --git a/packages/ember-routing-htmlbars/tests/utils.js b/packages/ember-routing-htmlbars/tests/utils.js index 98dce7c8a9c..65464409e95 100644 --- a/packages/ember-routing-htmlbars/tests/utils.js +++ b/packages/ember-routing-htmlbars/tests/utils.js @@ -10,7 +10,6 @@ import Controller from "ember-runtime/controllers/controller"; import ObjectController from "ember-runtime/controllers/object_controller"; import ArrayController from "ember-runtime/controllers/array_controller"; -import _MetamorphView from "ember-views/views/metamorph_view"; import EmberView from "ember-views/views/view"; import EmberRouter from "ember-routing/system/router"; import { @@ -56,7 +55,6 @@ function buildRegistry(namespace) { registry.register("controller:object", ObjectController, { instantiate: false }); registry.register("controller:array", ArrayController, { instantiate: false }); - registry.register("view:default", _MetamorphView); registry.register("view:toplevel", EmberView.extend()); registry.register("view:-outlet", OutletView); registry.register("view:core-outlet", CoreOutletView); diff --git a/packages/ember-routing-views/lib/views/outlet.js b/packages/ember-routing-views/lib/views/outlet.js index 87e1b6e7048..fc09b091f05 100644 --- a/packages/ember-routing-views/lib/views/outlet.js +++ b/packages/ember-routing-views/lib/views/outlet.js @@ -4,8 +4,6 @@ */ import View from "ember-views/views/view"; -import { _Metamorph } from "ember-views/views/metamorph_view"; - import topLevelViewTemplate from "ember-htmlbars/templates/top-level-view"; topLevelViewTemplate.revision = 'Ember@VERSION_STRING_PLACEHOLDER'; @@ -40,4 +38,4 @@ export var CoreOutletView = View.extend({ } }); -export var OutletView = CoreOutletView.extend(_Metamorph); +export var OutletView = CoreOutletView.extend({ tagName: '' }); diff --git a/packages/ember-routing/lib/system/router.js b/packages/ember-routing/lib/system/router.js index 3d571135c7c..6af0acb4e4c 100644 --- a/packages/ember-routing/lib/system/router.js +++ b/packages/ember-routing/lib/system/router.js @@ -356,24 +356,20 @@ var EmberRouter = EmberObject.extend(Evented, { this.reset(); }, - _lookupActiveView(templateName) { - var active = this._activeViews[templateName]; - return active && active[0]; + _lookupActiveComponentNode(templateName) { + return this._activeViews[templateName]; }, - _connectActiveView(templateName, view) { - var existing = this._activeViews[templateName]; - - if (existing) { - existing[0].off('willDestroyElement', this, existing[1]); - } + _connectActiveComponentNode(templateName, componentNode) { + Ember.assert('cannot connect an activeView that already exists', !this._activeViews[templateName]); + var _activeViews = this._activeViews; function disconnectActiveView() { - delete this._activeViews[templateName]; + delete _activeViews[templateName]; } - this._activeViews[templateName] = [view, disconnectActiveView]; - view.one('willDestroyElement', this, disconnectActiveView); + this._activeViews[templateName] = componentNode; + componentNode.renderNode.addDestruction({ destroy: disconnectActiveView }); }, _setupLocation() { diff --git a/packages/ember-views/lib/mixins/view_child_views_support.js b/packages/ember-views/lib/mixins/view_child_views_support.js index 03f30133c51..0fd52c5d917 100644 --- a/packages/ember-views/lib/mixins/view_child_views_support.js +++ b/packages/ember-views/lib/mixins/view_child_views_support.js @@ -95,10 +95,8 @@ var ViewChildViewsSupport = Mixin.create({ view = maybeViewClass.create(attrs); - // don't set the property on a virtual view, as they are invisible to - // consumers of the view API if (view.viewName) { - set(get(this, 'concreteView'), view.viewName, view); + set(this, view.viewName, view); } } else if ('string' === typeof maybeViewClass) { var fullName = 'view:' + maybeViewClass; diff --git a/packages/ember-views/lib/views/core_view.js b/packages/ember-views/lib/views/core_view.js index 6ef5093526c..b67fd3534aa 100644 --- a/packages/ember-views/lib/views/core_view.js +++ b/packages/ember-views/lib/views/core_view.js @@ -9,7 +9,6 @@ import Evented from "ember-runtime/mixins/evented"; import ActionHandler from "ember-runtime/mixins/action_handler"; import { get } from "ember-metal/property_get"; -import { computed } from "ember-metal/computed"; import { typeOf } from "ember-metal/utils"; import { internal } from "htmlbars-runtime"; @@ -40,7 +39,6 @@ var renderer; */ var CoreView = EmberObject.extend(Evented, ActionHandler, { isView: true, - isVirtual: false, _states: cloneStates(states), @@ -74,15 +72,6 @@ var CoreView = EmberObject.extend(Evented, ActionHandler, { _state: null, - // return the current view, not including virtual views - concreteView: computed('parentView', function() { - if (!this.isVirtual) { - return this; - } else { - return get(this, 'parentView.concreteView'); - } - }), - instrumentName: 'core_view', instrumentDetails(hash) { diff --git a/packages/ember-views/lib/views/metamorph_view.js b/packages/ember-views/lib/views/metamorph_view.js index 49f5bad3401..2d77117ea1f 100644 --- a/packages/ember-views/lib/views/metamorph_view.js +++ b/packages/ember-views/lib/views/metamorph_view.js @@ -18,7 +18,6 @@ import { Mixin } from "ember-metal/mixin"; @private */ export var _Metamorph = Mixin.create({ - isVirtual: true, tagName: '', instrumentName: 'metamorph', diff --git a/packages/ember-views/lib/views/states/in_dom.js b/packages/ember-views/lib/views/states/in_dom.js index 1873f3cb84d..42e93291e14 100644 --- a/packages/ember-views/lib/views/states/in_dom.js +++ b/packages/ember-views/lib/views/states/in_dom.js @@ -15,7 +15,7 @@ merge(inDOM, { enter(view) { // Register the view for event handling. This hash is used by // Ember.EventDispatcher to dispatch incoming events. - if (!view.isVirtual) { + if (view.tagName !== '') { view._register(); } diff --git a/packages/ember-views/lib/views/view.js b/packages/ember-views/lib/views/view.js index 20e7cd42425..742d2a13a35 100644 --- a/packages/ember-views/lib/views/view.js +++ b/packages/ember-views/lib/views/view.js @@ -1253,7 +1253,7 @@ var View = CoreView.extend( @private */ init() { - if (!this.isVirtual && !this.elementId) { + if (this.tagName !== '' && !this.elementId) { this.elementId = guidFor(this); } diff --git a/packages/ember-views/tests/system/event_dispatcher_test.js b/packages/ember-views/tests/system/event_dispatcher_test.js index b4b18eacbb8..05254db1023 100644 --- a/packages/ember-views/tests/system/event_dispatcher_test.js +++ b/packages/ember-views/tests/system/event_dispatcher_test.js @@ -180,6 +180,7 @@ QUnit.skip('should not interfere with event propagation of virtualViews', functi var receivedEvent; var view = View.create({ + // FIXME: isVirtual is no longer a thing isVirtual: true, render(buffer) { buffer.push('
'); diff --git a/packages/ember-views/tests/views/metamorph_view_test.js b/packages/ember-views/tests/views/metamorph_view_test.js index 74ea718301c..4764b7b433c 100644 --- a/packages/ember-views/tests/views/metamorph_view_test.js +++ b/packages/ember-views/tests/views/metamorph_view_test.js @@ -9,6 +9,57 @@ import _MetamorphView from "ember-views/views/metamorph_view"; var view, childView, metamorphView; QUnit.module("Metamorph views", { + setup() { + view = EmberView.create({ + template: compile('parent{{view view.metamorphView}}') + }); + }, + + teardown() { + run(function() { + view.destroy(); + if (childView && !childView.isDestroyed) { + childView.destroy(); + } + + if (metamorphView && !metamorphView.isDestroyed) { + metamorphView.destroy(); + } + }); + } +}); + +QUnit.test("a Metamorph view has not tag, is in the view tree", function() { + childView = EmberView.create({ + template: compile('child') + }); + + metamorphView = _MetamorphView.create({ + template: compile('metamorph{{view view.childView}}'), + childView + }); + + view.set('metamorphView', metamorphView); + + run(function() { + view.appendTo("#qunit-fixture"); + }); + + equal(get(childView, 'parentView'), metamorphView, "metamorph views are childViews"); + + var children = get(view, 'childViews'); + + equal(get(children, 'length'), 1, "precond - there is only one child of the main node"); + equal(children.objectAt(0), metamorphView, "metamorph views are child views"); + + ok(view.$().html().match(/parentmetamorph]*>child<\/div>/), 'metamorph views have not tag'); +}); + +// FIXME: Many of the following tests presume isVirtual behavior where metamorphs are +// not part of the view hierarchy. This has been changed post-glimmer, and they will +// now be part of the view tree. + +QUnit.module("Metamorph views with render", { setup() { view = EmberView.create({ render(buffer) { diff --git a/packages/ember/tests/routing/basic_test.js b/packages/ember/tests/routing/basic_test.js index 49d669a72ec..15ceff98b05 100644 --- a/packages/ember/tests/routing/basic_test.js +++ b/packages/ember/tests/routing/basic_test.js @@ -3856,7 +3856,7 @@ QUnit.test("Can disconnect from the render helper's children", function() { equal(Ember.$('#qunit-fixture .foo .index').text(), ''); }); -QUnit.skip("Can render({into:...}) nested render helpers", function() { +QUnit.test("Can render({into:...}) nested render helpers", function() { Ember.TEMPLATES.application = compile('{{render "foo"}}'); Ember.TEMPLATES.foo = compile('
{{render "bar"}}
'); Ember.TEMPLATES.bar = compile('
{{outlet}}
'); @@ -3884,7 +3884,7 @@ QUnit.skip("Can render({into:...}) nested render helpers", function() { equal(Ember.$('#qunit-fixture .bar').text(), 'baz'); }); -QUnit.skip("Can disconnect from nested render helpers", function() { +QUnit.test("Can disconnect from nested render helpers", function() { Ember.TEMPLATES.application = compile('{{render "foo"}}'); Ember.TEMPLATES.foo = compile('
{{render "bar"}}
'); Ember.TEMPLATES.bar = compile('
{{outlet}}
');