Skip to content

Instantly share code, notes, and snippets.

@stefanpenner
Last active December 17, 2015 14:49
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save stefanpenner/5627411 to your computer and use it in GitHub Desktop.
Save stefanpenner/5627411 to your computer and use it in GitHub Desktop.

This document aims to help aggregate issues and solutions around the defaultContainer deprecation.

The Error Message:

Using the defaultContainer is no longer supported. [defaultContainer#lookup]

breaux, where's my defaultContainer?

Well it is still there, and will continue to work for some time. Likely it will be gone by 1.0.0 final

Why is this happening.

We recieved several issues, these issues were non-obvious but it appeared that they where caused by the defaultContainer leaking between tests. This obviously lead to very unexpected test failures. The quick fix was obviously to ensure the defaultContainer was purged between tests. Upon further investigation, almost all the issues turned out to be childViews which were not wired up correctly. For example not using this.createChildView on creation of childViews. Ultimately, the existence of the defaultContainer was masking the reported issues, and likely many more. In addition the obvious issues, we are trying to remove our dependency on globals (but that is another story).

migration paths: (WIP)

if you are creating dynamic views in the context of the parentView, you should be using:

this.createChildView(viewClass, viewAttributes)

if you are creating views outside the context of a parentView (this may not be recommended, but it is happening) you will want to make sure to instantiate your view via the container itself.

this.container.lookup('view:apple')
// will provide a instance of apple view.

how container lookups are resolved

Action items

  • appendTo another ember view, should then grab the container of that view.
  • continue to flush out this gist
  • improve docs/guides
  • improve deprecation warnings
  • explore methods where this works without developer thought (without perf regressions)

fixes in the wild:

But I have a legitmate usecase not expressed here.

Post a comment, describing the issue, and hopefully a jsbin or jsfiddle demonstrating the issue. As time permits, a solution or migration path will finds its way up into this document.

Q/A

Q: can we not have views get a container by default? The App gets a container right?

A: App is the root, and as such it creates the initial container.

Q: So when I do createChildView, which container is that using?

A: typically, it is using the container passed down the view hierarchy, and this typically occurs at creation.

Q: Could we set the container when you append it to a container view's childViews array?

A: likely, but its not just the container, it is a place for the framework to wire-up other things. https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/view.js#L2072

(ask questions in the comments and I will hoist them here.)

@stefanpenner
Copy link
Author

@cyril-sf, @pivotal-medici, @MikeAski & @fanta thanks for your corrections.

@nightire
Copy link

nightire commented Jun 2, 2013

I'm not quite sure whether I understand this correctly, I've encountered this issue just now when I try to do this:

window.App = Ember.Application.create({
  ready: function() {
    App.AChildView.create().append();
  }
})

Ok, I got what I want in browser, but meantime Ember.js throw me a deprecation warning:

Using the defaultContainer is no longer supported. [defaultContainer#lookup]

After read your article, I'm still confused about how to do this right. Is this means the ApplicationView is not a ContainerView, so I should create & append the AChildView in another place? or I was totally do this in the wrong way?

@edimoldovan
Copy link

@stefanpenner
Hey Stefan,
In this case, how should I use createChildView? I seam to have a problem with it. Also, I would like to pass some custom context to those views. How should I do that in here?

for (i=0;i<portlets.length;i+=1) {
    index = Ember.String.classify(portlets[i].name) + "View";
    newView = App[index].create();
    App.LayoutContainer.pushObject(newView);
}

@pivotal-medici
Copy link

@stefanpenner how do you propose we handle setting the currentView?

-- AK

@stefanpenner
Copy link
Author

@pivotal-medici & @edimoldovan emberjs/ember.js#2821 should handle your scenarios.

@nightire by default the ApplicationView is correctly appended to the rootElement for you, container and all. This view will be setup correctly and all its children and descendants should also be setup for you. If you choose to append a view yourself, which is totally allowed, although not needed in many situations, you must setup the dependencies correctly yourself.

When appending a root view yourself, you will typically do this within the router, not from Application#ready. That being said, ember is flexible and allows you to solve the problem in many ways. Can you provide a more detail example so i can understand your intent?

@edimoldovan
Copy link

That looks perfect, let me give that a delightful tryout! :) Thank you!

@stefanpenner
Copy link
Author

@edimoldovan let me know how it goes.

@pixelchild
Copy link

@nightire I was doing the exact same.. I've ended up changing it for something like this instead

App.IndexRoute = Ember.Route.extend({
    setupController: function (controller)
    {
         App.AChildView.create({container: this.container}).append();
    }
});

@dev-inigmas
Copy link

So... I have an interesting scenario that I don't see brought up here yet (either that or I'm just not understanding something).

I've been in the process of creating a rather large CRUD application with 14 data types to manage (more will be coming later); so as I've been creating this application, I've been developing a "component" library that is capable of auto-generating Twitter Bootstrap forms from JSON.

For one of my newest form components, the user can see a list of items, edit them, remove them, or add to them. When adding or editing, a generated Modal Form pops up to capture the user's input with the Modal's own Form Element.

So... the Ember View that creates the list and "Add" button are all inside of the "main" Form; meanwhile the Modal needs to be added to the DOM so that it can expose it's own Form.

I only just got that bit working today... before, I just didn't have the Modal expose a Form, and it was just embedded in the parent view. But then there were interesting side-effects when property names collide and just the general lack of HTML5 Form Validation.

Here is how my "ItemList" component is currently managing the ModalForm. (Note: It's pretty rough at the moment since I'm really just trying to get things to work)

App.ItemListView = App.ControlGroupView.extend
  templateName: 'bootstrap/itemList'

  didInsertElement: ->
    @_super()

    modalName = @get 'content.property'

    modalForm = App.ModalFormView.create
      parentView        : @
      contentBinding    : 'parentView.content'
      componentsBinding : 'content.edit'
      name              : modalName
      elementId         : modalName + "Modal"
    modalForm.append()

    @set 'modalForm', modalForm

  willDestroyElement: ->
    @_super()
    @modalForm.remove()

So, if there's a much better way to do something like this, please let me know. If not, I'd prefer to be able to append Views to the DOM when needed. It isn't often.

@dev-inigmas
Copy link

Never mind my earlier comments. For some reason I thought that View.append() was deprecated. And then, I didn't realize that, "this.container.lookup('view:apple')" would actually create a new instance of the view.

So, for anyone else as confused as I was, just replace your View.create call with "this.container.lookup(...)".

@Adriaaaaan
Copy link

So does this mean we can't use layoutName in views anymore??

App = Ember.Application.create();

App.aView = Ember.View.extend({
    layoutName:'index',
    templateName:'lorem'
});
App.aView.create().append();

I haven't seen any mention of how to use the layoutName.

@H1D
Copy link

H1D commented Aug 6, 2013

@stefanpenner how to unit test views now? To get a controller I need to initialize application.
Restarting whole application at each unit-test seems weird and slow.

Now I'm using this approach:

test 'a test', ->
    a_view = App.AView.create();
    return Em.run ->
      a_view.appendTo($('#fixture'));

@stefanpenner
Copy link
Author

@H1D you merely need to create a container per unit test, and ensure the view has a container, and the template can be fetched from the container.

This ensues proper isolation.

@knownasilya
Copy link

@dlpatri thanks for clarifying that. Is it possible to pass a hash to the lookup? I just used setters after the fact, which works as well.

@leonelag
Copy link

@stefanpenner Care to update the Views guide at http://emberjs.com/guides/views/defining-a-view/ ?
As an Ember noob, on my first 20 minutes following it, I have just been bitten by this commit.

@mayatskiy
Copy link

@velesin
Copy link

velesin commented Nov 12, 2013

@stefanpenner "you merely need to create a container per unit test, and ensure [...] the template can be fetched from the container". Can you provide some example how this should be correctly configured? Right now in my tests I'm using (probably not correct) hack:

Ember.View.create({ container: App.__container__ });

From your comment I understood that I should be creating a new, isolated container instance for my tests (plus configure it somehow so the template can be fetched from it) - but I have no idea what is the correct way to do this?

@stefanpenner
Copy link
Author

@velesin

container.register('view:foo', FooView , { singleton true });
container.lookup('view:foo') // instance of theview

@jmorvan
Copy link

jmorvan commented Jan 7, 2014

How should I proceed in the case where I am creating dynamic view from a loop:

switch (itm.type) {
            case "Page":
                view = Ember.ContainerView.create({classNames: itm.options.class, elementId: itm.id});
                break;
            case "Row":
                view = Ember.ContainerView.create({classNames: itm.options.class, elementId: itm.id});
                break;
            case "Container":
                view = Ember.ContainerView.create({classNames: itm.options.class, elementId: itm.options.id});
                break;
}

Am I doing this wrong by extending views in this manner?

I could potentially have dozens of different itm.type and I would like to avoid having to explicitly declare a class for each.

@H1D
Copy link

H1D commented Apr 3, 2014

Here is the setup I'm using:


post_view = container = null
module "Post View",
  setup: ->
    container = new Em.Container
    container.optionsForType 'template', { instantiate: false }
    container.register 'template:post', Ember.Handlebars.compile('{{text}}')

    post_view = App.PostView.create(context: {text:'foo'}, container: container)
    Em.run ->
      post_view.createElement()
      post_view.set 'context', step
  teardown: ->
    Ember.run ->
      post_view.destroy()
      container.destroy()

test "renders 'text' property", 1, ->
  equal post_view.$().text(), 'foo', 'rendered text doesn\'t match context'

Note container.optionsForType, otherwise ember will try to call create() on template (i.e. instantiate). Also I was surprised that Em.Container is probably the only object you have to instantiate using new

@happycollision
Copy link

I changed a bit of my code based on a suggestion by @pixelchild for @nightire to no avail:

App.SomeRoute = Ember.Route.extend({
    someFunction: function(){
        App.myViewHolder = App.MyView.create({container: this.container});
        App.myViewHolder.append();
    }
});

I am still getting this deprecation warning when someFunction is called. 😕

All I really want to do is append a couple elements to the DOM when I am in a certain route.

Update: Seems that it was actually a run loop issue. I was changing my target to window by mistake.

@tachang
Copy link

tachang commented Jun 30, 2014

I'm lost.

window.Application = Ember.Application.create();

var view = Ember.View.create({
templateName: 'info',
name: "Bob"
});
view.appendTo(''#info');

I do this and get this warning. But this is from the docs: http://emberjs.com/guides/views/defining-a-view/

Copy link

ghost commented Jul 7, 2014

Please update the guide.

@stefanpenner
Copy link
Author

@hannicolas im unsure what needs to be updated?

@richardkmichael
Copy link

@stefanpenner The example in the guide poses problems for newcomers (me). I can't tell you what it should say (because I'm just learning Ember), but as @tachang also mentions, I expected (something like the guide's example):

<!-- index.html -->
...
<head>
  <!-- Other script tags for Ember itself and dependencies omitted. -->
  <script type="x-handlebars" data-template-name="foo-view-template">
    Hello, World! from foo view
  </script>
  <script src="myapp.js"></script>
</head>
// myapp.js -- only this content, nothing else.

var app = Ember.Application.create();  // Is this even necessary?

var fooView = Ember.View.create({
  templateName: "foo-view-template"
});

fooView.append();

.. to display a page containing:

<body>
  <!-- Possibly other stuff in index.html, followed by... -->
  Hello World! from foo view
</body>

.. but instead the console prints (the git.io link points to this gist):

Uncaught Error: Container was not found when looking up a views template. This is most likely due to manually instantiating an Ember.View. See: http://git.io/EKPpnA

@supairish
Copy link

@richardkmichael I'm having the same problem, did you find a fix?

@johnmcdowall
Copy link

@richardkmichael @supairish @stefanpenner I've found that changing from .create to .extend from making new view for insertion prevents the deprecation warning from being triggered when you set a templateName. YMMV.

@avaz
Copy link

avaz commented Oct 26, 2014

The docs of Route.loading event is showing an example of the old way: [http://emberjs.com/api/classes/Ember.Route.html#event_loading]
How this would be with the this.container.lookup strategy? I mean, what is the best way to pass the 'classNames' is the example in the Route.loading of create and append views?
I'm willing to update the docs but I'm don't know for what put in the example.

@markmilman
Copy link

I'm trying to use Ember component in an existing rails app as part of gradually migrating the view layer created by rails to Ember and form a single page app.
I have used the following code, which works in 1.5.0 but throws the deprecation warning (and therefore breaks in 1.8) . Will appreciate any help to get this working in 1.8.0:

Sbcx.MaterialsListComponent = Ember.Component.extend({
  layoutName: 'components/materials-list'
  materialList: null

  actions: {
    remove: (m)->
      console.log('removed ' + m.get('name'))
      m.destroyRecord()
  }
})


$(document).ready ->

  Sbcx.store.find("material").then (result) ->
    $(".component-placeholder").each ->
      # Build the component & stuff in any pre-existing value
      component = Sbcx.MaterialsListComponent.create(materialList: result)

      # Gut out the container div & replace with the component
      component.replaceIn this

@yanni4night
Copy link

This is really confused,I am going to skip this page guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment