diff --git a/packages/ember-application/lib/system/application.js b/packages/ember-application/lib/system/application.js index c06c03427ae..3bc29b4d604 100644 --- a/packages/ember-application/lib/system/application.js +++ b/packages/ember-application/lib/system/application.js @@ -331,6 +331,10 @@ var Application = Namespace.extend(DeferredMixin, { buildDefaultInstance: function() { var instance = this.buildInstance(); + // For the default instance only, set the view registry to the global + // Ember.View.views hash for backwards-compatibility. + EmberView.views = instance.container.lookup('-view-registry:main'); + // TODO2.0: Legacy support for App.__container__ // and global methods on App that rely on a single, // default instance. @@ -1006,6 +1010,10 @@ Application.reopenClass({ registry.register('view:select', SelectView); registry.register('view:-outlet', OutletView); + registry.register('-view-registry:main', { create: function() { return {}; } }); + + registry.injection('view', '_viewRegistry', '-view-registry:main'); + registry.register('view:default', _MetamorphView); registry.register('view:toplevel', EmberView.extend()); diff --git a/packages/ember-application/tests/system/visit_test.js b/packages/ember-application/tests/system/visit_test.js index 16d28dc0ac0..57bb8fbda56 100644 --- a/packages/ember-application/tests/system/visit_test.js +++ b/packages/ember-application/tests/system/visit_test.js @@ -2,6 +2,7 @@ import run from "ember-metal/run_loop"; import Application from "ember-application/system/application"; import ApplicationInstance from "ember-application/system/application-instance"; import Router from "ember-routing/system/router"; +import View from "ember-views/views/view"; import compile from "ember-template-compiler/system/compile"; function createApplication() { @@ -82,4 +83,45 @@ if (Ember.FEATURES.isEnabled('ember-application-visit')) { assert.ok(false, "The visit() promise was rejected: " + error); }); }); + + QUnit.test("Views created via visit() are not added to the global views hash", function(assert) { + QUnit.expect(6); + QUnit.stop(); + + var app; + + run(function() { + app = createApplication(); + app.instanceInitializer({ + name: 'register-application-template', + initialize: function(app) { + app.registry.register('template:application', compile('

Hello world

{{view "child"}}')); + app.registry.register('view:application', View.extend({ + elementId: 'my-cool-app' + })); + app.registry.register('view:child', View.extend({ + elementId: 'child-view' + })); + } + }); + }); + + assert.equal(Ember.$('#qunit-fixture').children().length, 0, "there are no elements in the fixture element"); + + app.visit('/').then(function(instance) { + QUnit.start(); + assert.ok(instance instanceof ApplicationInstance, "promise is resolved with an ApplicationInstance"); + + run(instance.view, 'appendTo', '#qunit-fixture'); + assert.equal(Ember.$("#qunit-fixture > #my-cool-app h1").text(), "Hello world", "the application was rendered once the promise resolves"); + assert.strictEqual(View.views['my-cool-app'], undefined, "view was not registered globally"); + ok(instance.container.lookup('-view-registry:main')['my-cool-app'] instanceof View, "view was registered on the instance's view registry"); + ok(instance.container.lookup('-view-registry:main')['child-view'] instanceof View, "child view was registered on the instance's view registry"); + + instance.destroy(); + }, function(error) { + QUnit.start(); + assert.ok(false, "The visit() promise was rejected: " + error); + }); + }); } diff --git a/packages/ember-views/lib/views/states/in_dom.js b/packages/ember-views/lib/views/states/in_dom.js index 19c5b799e31..bbdc6588fb0 100644 --- a/packages/ember-views/lib/views/states/in_dom.js +++ b/packages/ember-views/lib/views/states/in_dom.js @@ -1,4 +1,3 @@ -import Ember from "ember-metal/core"; // Ember.assert import create from 'ember-metal/platform/create'; import merge from "ember-metal/merge"; import EmberError from "ember-metal/error"; @@ -12,17 +11,12 @@ import hasElement from "ember-views/views/states/has_element"; var inDOM = create(hasElement); -var View; - merge(inDOM, { enter: function(view) { - if (!View) { View = requireModule('ember-views/views/view')["default"]; } // ES6TODO: this sucks. Have to avoid cycles... - // Register the view for event handling. This hash is used by // Ember.EventDispatcher to dispatch incoming events. if (!view.isVirtual) { - Ember.assert("Attempted to register a view with an id already in use: "+view.elementId, !View.views[view.elementId]); - View.views[view.elementId] = view; + view._register(); } Ember.runInDebug(function() { @@ -33,10 +27,8 @@ merge(inDOM, { }, exit: function(view) { - if (!View) { View = requireModule('ember-views/views/view')["default"]; } // ES6TODO: this sucks. Have to avoid cycles... - if (!this.isVirtual) { - delete View.views[view.elementId]; + view._unregister(); } }, diff --git a/packages/ember-views/lib/views/view.js b/packages/ember-views/lib/views/view.js index 706b7272530..873a567b8ab 100644 --- a/packages/ember-views/lib/views/view.js +++ b/packages/ember-views/lib/views/view.js @@ -1305,6 +1305,10 @@ var View = CoreView.extend( } this._super.apply(this, arguments); + + if (!this._viewRegistry) { + this._viewRegistry = View.views; + } }, __defineNonEnumerable: function(property) { @@ -1371,6 +1375,33 @@ var View = CoreView.extend( return this.currentState.handleEvent(this, eventName, evt); }, + /** + Registers the view in the view registry, keyed on the view's `elementId`. + This is used by the EventDispatcher to locate the view in response to + events. + + This method should only be called once the view has been inserted into the + DOM. + + @method _register + @private + */ + _register: function() { + Ember.assert("Attempted to register a view with an id already in use: "+this.elementId, !this._viewRegistry[this.elementId]); + this._viewRegistry[this.elementId] = this; + }, + + /** + Removes the view from the view registry. This should be called when the + view is removed from DOM. + + @method _unregister + @private + */ + _unregister: function() { + delete this._viewRegistry[this.elementId]; + }, + registerObserver: function(root, path, target, observer) { if (!observer && 'function' === typeof target) { observer = target;