diff --git a/packages/ember-views/lib/views/container_view.js b/packages/ember-views/lib/views/container_view.js index d239cfa93e8..9889cd20eea 100644 --- a/packages/ember-views/lib/views/container_view.js +++ b/packages/ember-views/lib/views/container_view.js @@ -175,13 +175,34 @@ var childViewsProperty = Ember.computed(function() { And the `Ember.View` instance stored in `aContainer.aView` will be removed from `aContainer`'s `childViews` array. - ## Templates and Layout A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or `defaultLayout` property on a container view will not result in the template or layout being rendered. The HTML contents of a `Ember.ContainerView`'s DOM representation will only be the rendered HTML of its child views. + ## Binding a View to Display + + If you would like to display a single view in your ContainerView, you can set its `currentView` + property. When the `currentView` property is set to a view instance, it will be added to the + ContainerView's `childViews` array. If the `currentView` property is later changed to a + different view, the new view will replace the old view. If `currentView` is set to `null`, the + last `currentView` will be removed. + + This functionality is useful for cases where you want to bind the display of a ContainerView to + a controller or state manager. For example, you can bind the `currentView` of a container to + a controller like this: + + // Controller + App.appController = Ember.Object.create({ + view: Ember.View.create({ + templateName: 'person_template' + }) + }); + + // Handlebars template + {{view Ember.ContainerView currentViewBinding="App.appController.view"}} + @extends Ember.View */ @@ -319,7 +340,27 @@ Ember.ContainerView = Ember.View.extend({ } else { this.domManager.prepend(this, view); } - } + }, + + currentView: null, + + _currentViewWillChange: Ember.beforeObserver(function() { + var childViews = get(this, 'childViews'), + currentView = get(this, 'currentView'); + + if (currentView) { + childViews.removeObject(currentView); + } + }, 'currentView'), + + _currentViewDidChange: Ember.observer(function() { + var childViews = get(this, 'childViews'), + currentView = get(this, 'currentView'); + + if (currentView) { + childViews.pushObject(currentView); + } + }, 'currentView') }); // Ember.ContainerView extends the default view states to provide different diff --git a/packages/ember-views/tests/views/container_view_test.js b/packages/ember-views/tests/views/container_view_test.js index 3680b6d3be2..e16a90a4c82 100644 --- a/packages/ember-views/tests/views/container_view_test.js +++ b/packages/ember-views/tests/views/container_view_test.js @@ -1,11 +1,10 @@ -var get = Ember.get, getPath = Ember.getPath; +var get = Ember.get, getPath = Ember.getPath, set = Ember.set; module("ember-views/views/container_view_test"); test("should be able to insert views after the DOM representation is created", function() { var container = Ember.ContainerView.create({ classNameBindings: ['name'], - name: 'foo' }); @@ -112,3 +111,140 @@ test("views that are removed from a ContainerView should have their child views }); equal(getPath(view, 'childViews.length'), 0, "child views are cleared when removed from container view"); }); + +test("if a ContainerView starts with an empy currentView, nothing is displayed", function() { + var container = Ember.ContainerView.create(); + + Ember.run(function() { + container.appendTo('#qunit-fixture'); + }); + + equal(container.$().text(), '', "has a empty contents"); + equal(getPath(container, 'childViews.length'), 0, "should not have any child views"); +}); + +test("if a ContainerView starts with a currentView, it is rendered as a child view", function() { + var container = Ember.ContainerView.create(); + var mainView = Ember.View.create({ + template: function() { + return "This is the main view."; + } + }); + + set(container, 'currentView', mainView); + + Ember.run(function() { + container.appendTo('#qunit-fixture'); + }); + + equal(container.$().text(), "This is the main view.", "should render its child"); + equal(getPath(container, 'childViews.length'), 1, "should have one child view"); + equal(getPath(container, 'childViews').objectAt(0), mainView, "should have the currentView as the only child view"); +}); + +test("if a ContainerView starts with no currentView and then one is set, the ContainerView is updated", function() { + var container = Ember.ContainerView.create(); + var mainView = Ember.View.create({ + template: function() { + return "This is the main view."; + } + }); + + Ember.run(function() { + container.appendTo('#qunit-fixture'); + }); + + equal(container.$().text(), '', "has a empty contents"); + equal(getPath(container, 'childViews.length'), 0, "should not have any child views"); + + Ember.run(function() { + set(container, 'currentView', mainView); + }); + + equal(container.$().text(), "This is the main view.", "should render its child"); + equal(getPath(container, 'childViews.length'), 1, "should have one child view"); + equal(getPath(container, 'childViews').objectAt(0), mainView, "should have the currentView as the only child view"); +}); + +test("if a ContainerView starts with a currentView and then is set to null, the ContainerView is updated", function() { + var container = Ember.ContainerView.create(); + var mainView = Ember.View.create({ + template: function() { + return "This is the main view."; + } + }); + container.set('currentView', mainView); + + Ember.run(function() { + container.appendTo('#qunit-fixture'); + }); + + equal(container.$().text(), "This is the main view.", "should render its child"); + equal(getPath(container, 'childViews.length'), 1, "should have one child view"); + equal(getPath(container, 'childViews').objectAt(0), mainView, "should have the currentView as the only child view"); + + Ember.run(function() { + set(container, 'currentView', null); + }); + + equal(container.$().text(), '', "has a empty contents"); + equal(getPath(container, 'childViews.length'), 0, "should not have any child views"); +}); + +test("if a ContainerView starts with a currentView and then is set to null, the ContainerView is updated", function() { + var container = Ember.ContainerView.create(); + var mainView = Ember.View.create({ + template: function() { + return "This is the main view."; + } + }); + container.set('currentView', mainView); + + Ember.run(function() { + container.appendTo('#qunit-fixture'); + }); + + equal(container.$().text(), "This is the main view.", "should render its child"); + equal(getPath(container, 'childViews.length'), 1, "should have one child view"); + equal(getPath(container, 'childViews').objectAt(0), mainView, "should have the currentView as the only child view"); + + Ember.run(function() { + set(container, 'currentView', null); + }); + + equal(container.$().text(), '', "has a empty contents"); + equal(getPath(container, 'childViews.length'), 0, "should not have any child views"); +}); + +test("if a ContainerView starts with a currentView and then a different currentView is set, the old view is removed and the new one is added", function() { + var container = Ember.ContainerView.create(); + var mainView = Ember.View.create({ + template: function() { + return "This is the main view."; + } + }); + + var secondaryView = Ember.View.create({ + template: function() { + return "This is the secondary view."; + } + }); + + container.set('currentView', mainView); + + Ember.run(function() { + container.appendTo('#qunit-fixture'); + }); + + equal(container.$().text(), "This is the main view.", "should render its child"); + equal(getPath(container, 'childViews.length'), 1, "should have one child view"); + equal(getPath(container, 'childViews').objectAt(0), mainView, "should have the currentView as the only child view"); + + Ember.run(function() { + set(container, 'currentView', secondaryView); + }); + + equal(container.$().text(), "This is the secondary view.", "should render its child"); + equal(getPath(container, 'childViews.length'), 1, "should have one child view"); + equal(getPath(container, 'childViews').objectAt(0), secondaryView, "should have the currentView as the only child view"); +});