Confused about Ember's design structure


#1

Hello,

I am new to Ember (only been using it about a week now), and I am very confused about the intended application design. From what I can tell so far, Ember sort of forces a particular design onto the application, and I am confused about why.

I have been told that I cannot have an MVC set (a model, a view, and a controller for a particular thing) without a route. To me, this is very confusing; I do not understand why Ember is forcing me to assign a navigable route to something that doesn’t necessarily need (or makes sense to have) a user interface.

What I would like to do, is to have all of the components of a page (not ‘components’ as in the Ember ‘component’) be in an MVC format. However, not all of these components should have a route assigned to them.

For example, if I had an application that allowed users to CRUD entries about books, I would want them to be able to set the genre. The list of available genres would be loaded via Ember-Data since it’s not hard-coded. However, I don’t want to have a route /genres, I just want to use the data in that model, or potentially use it as an MVC set (where genre would have a model AND a view and controller). I also don’t want a nested route such as /book/genre/, I just want to be able to include the genre list wherever and whenever, allowing it to be controlled by the controller, viewed with the view, and backed by the model.

I’ve scoured the Internet and asked on StackOverflow, but I am still very confused.

Any help or clarity is extremely appreciated. I am also well aware that I am probably incorrect in my understanding of how Ember works.


#2

I have been told that I cannot have an MVC set (a model, a view, and a controller for a particular thing) without a route.

Source? I dunno, I create one or more of those things without a route all the time.


#3

Are you talking about ember routes or your REST API URLs? When using Ember Data, you will need to have /genres URL in your API if you wish to store list of genres as masMany relation, but you would’t need to have one for, for example, comma-delimited string.

The reson why API should have an extra route is because it is recommended to follow JSON API conventions.

You centainly can create Ember.Controller or Ember.View without having related Ember.Route.


#4

Routes are about routable state - urls. If you don’t want an extra url then don’t create a route.

You can use the {{render}} helper to render a template, controller, view and optionally provide a specific model in your templates. This is probably what you are looking for.

Be aware that the “MVC” in ember is probably different to what you are used to - Model is the same, Views are an abstraction around DOM elements and are mostly used for handling DOM events or occasionally (to be avoided) manual DOM munging, and controllers are more of a “presenter”, they decorate your models and present them to the view and connect with other controllers through the “needs” api.

This isn’t necessarily true - if you want to load your genres at the same time as your books you can sideload them in the request to /books. This would be recommended for performance (fewer http requests)


#5

Thanks for all of your responses, but I’m still extremely confused.

I still can’t figure out how to properly put this list of genres onto a page about a book, to the point where I really don’t even know how to ask the question without pasting code.

I have a route/model/view/controller/template for Book. That all works fine. In the book template, I want to show the list of genres… so I made a mode/view/controller/template for genres:

Controller:

App.GenresControllerExtend = Ember.ArrayController.extend({});
App.GenresController = App.GenresControllerExtend.create();

// simulate a REST call to populate the controller (model??) with data
setTimeout(function () {
  App.GenresController.set('content', App.Genre.FIXTURES);
}, 500);

View:

App.GenresView = Ember.View.extend({
  controller: App.GenresController,
  templateName: 'genres'
});

Template:

<script type="text/x-handlebars" data-template-name="genres" id="genres">
  {{log controller}}
</script>

I then try to insert this view into the Book view with:

{{view WA.CategoriesView}}

Several questions:

  1. Is there any way to just use the Chrome console instead of the annoying Ember debugging methods? I have to use the ‘log’ Handlebars helper in order to get any useful data and I can’t use my beloved Chrome console. I tried using “App.container.lookup(‘controller:genres’);” as it says on the guide, but it returns “undefined is not a function”. If I use something like console.log(App.GenresController);, it just returns “App.GenresController” as text.

  2. If something isn’t part of a route, does it get all the naming-convention witchcraft? When I made the genres view, it wouldn’t work unless I specifically told it to use that specific controller and template. Is this because it doesn’t have a route? And does this affect all of the other places where the naming convention thing comes into play? For example, when “ExampleController” gets converte to “example” for use in the “needs” property of another controller. I read somewhere on StackOverflow that it is bad practice to explicitly set the controller for a view, but I see no other way. Again, I don’t want a route for genres. It doesn’t need a route and shouldn’t have one.

  3. Instead of using the {{view}} helper, I tried using the {{render}} helper as such:

    {{render “genres” genres}}

And that returned “undefined is not a function”, then I figured this was the wrong sort of helper entirely. The documentation says that the first parameter is the “context”, but I don’t know what that means in this situation. I figured it was just the variable off the Book model, but then I thought that’s what the second parameter was.

  1. When I create the controller, why do I first need to “extend” it, THEN create it? From my understanding, “extend” basically makes a subclass of the class being extended, but in my case they should be the same thing. I just want to create an instance of an ArrayController, not extend it into some other class first.

  2. Is Ember the right framework for me? From what I am seeing and from what I have heard, EmberJS likes to force a specific type of application structure on the developer, and if you don’t want to follow that structure, then you’re out of luck. Does Ember support the kind of things I want to do nicely, or would I have to hack it in with some way that defeats the purpose of using a framework at all? I want to have everything managed with MVC, but not at the level of routes. On one page, I’d like several things to have a model/view/controller, but most of them wouldn’t have routes.

  3. Is there a good way to learn this framework quickly? From what I’ve read, it takes people months to get a handle on this framework. In terms of learning curve, this might be the steepest I’ve encountered. I really don’t have months to learn it. From the taglines (“Getting started with Ember is easy!”, “More productive out of the box”) I thought it would be an easy task to get a simple application up and running, but it’s turning into quite a chore. The taglines are very misleading. The fact that ember does do much “magic” under the covers is one of ember’s selling points, but it also makes it exponentially more confusing for someone trying to learn how it works.

Thanks :confused:


#6

Yes, in fact, as far as I know, async defaults to false. Not to confuse @tangibleLime, more info on async can be found here.

You could try Ember Inspector.

Could you expand on this? Calling

App.GenresView = Ember.View.extend({
  foo: 'bar',
});

should be sufficent for controller to be registered in Ember, you wouldn’t need to specifically ‘create’ it.


#7

I tried again and it looks using just create() instead of extend()+create() works for me. I must have had some other configuration written incorrectly.


#8
App.GenresControllerExtend = Ember.ArrayController.extend({});

App.GenresController = App.GenresControllerExtend.create();

// simulate a REST call to populate the controller (model??) with data
setTimeout(function () {
  App.GenresController.set('content', App.Genre.FIXTURES);
}, 500);

This is very wrong, you seem to be confused about the difference between classes and instances.

You should have:

App.GenresController = Em.ArrayController.extend()

Ember will create the controller for you. You should pretty much never call create() on a controller. Ember must create the controller instances for you.

You should generally load the data into the controller in the model hook of a route:

App.GenreRoute = Em.Route.extend({
  model: function() {
    return App.Genre.FIXTURES;
  }
});

You can simulate ajax there if you want, by returning a promise:

App.GenreRoute = Em.Route.extend({
  model: function() {
    return new Em.RSVP.Promise(function(resolve, reject) {
     // or add setTimeout if desired.
      resolve(App.Genre.FIXTURES);
    }
  }
})

Use the ember extension for this as mentioned by @muchweb

As mentioned you have been defining your controllers wrongly. Use the render helper.

You should be using the render helper for this.

Generally you assign content to controllers in a route, then you can use the render helper to render them. The render helper renders a controller, template and view combo, and you can optionally pass in a context.

In your case, it sounds like you don’t need a route just for genres. That’s fine, in your existing route you can setup the controller appropriately using controllerFor:

App.BooksRoute = Em.Route.extend({
  model: function() {
    return Em.RSVP.hash({
      books: $.getJSON('/books'),
      genres: $.getJSON('/genres')
    });
  },

  setupController: function (controller, context) {
    this._super(controller, context.books); // this assigns the books to the BooksController
    this.controllerFor('genres', context.genres);
  }
})

Now you can use {{render ‘genres’}} in a template.

You don’t. You should never manually create a controller. Ember will create the instances for you.

Ember supports what you are trying to do nicely, trivially in fact, with very little code. It just has to be the right code.

There are several books out, or you could go to a training course. But all the info you need is in the guides, and then the API docs. You should make sure to read all the guides in order, and consult the API docs for further information. Ember does have a steep learning curve but ultimately the decision to use it is up to you. I have found that it saves me massive amounts of time now that I know it.


#9

If you are still confused, I’ve created a JSBin application, that lists books and genres, having only one IndexRoute Ember route.

I hope it will make clear most of your questions.