The "right" way to load additional models (to build filtering checkboxes)


#1

I recently answered a question on stackoverflow which bothered me a bit… and I was wondering if this is the “right way” to do it. It’s not really a question for stack overflow, it’s more a discussion… and possibly I might try to see if I can’t work this into the ember guides somehow because it’s a question which seems to crop up a lot.

It turns out what he wanted to do was have a list of anime (which were categorized - by using an asscoiation, with categories) which he could filter based on some checkboxes for each category.

The thing that I wasn’t sure if I’d solved in the “best way” was that he was using a setupController method on the route to load and set the categories… that doesn’t seem like it was right to me… because it would happen async… it felt to me like the category loading code should actually be in the model part of the route, not the setupController… if someone with a bit more experience could chime in on this, that’d make me a very happy chappy indeed. :slight_smile: At that point, I feel it’d be very useful to have that example actually added to the ember docs, and I’d try to go about getting that in place as a guide or something.

This is the jsbin that I ended up creating: http://jsbin.com/datojebu/11/edit

It was in answer to this question: http://stackoverflow.com/questions/23155621/how-to-manage-multiple-checkboxes-in-ember/23179899#23179899

Any help would be greatly appreciated.


#2

Any opinion on that ? I’ve been doing it in setupController as well and it doesn’t seem right.


#3

It’s probably best to use an RSVP.hash most likely:

http://emberjs.com/api/classes/Ember.RSVP.html#method_hash

Seems a bit strange, though, when one of the objects will be the main object that should be the model, and the rest will be the side loading objects for selection-option-lists, etc.


#4

I’ve seen the RSVP approach mentioned, people saying that by using the model hook you get the built-in error handling. But I haven’t really liked the idea of returning the RSVP as the model object a was recommended. I’m now trying the pattern of having an auxModels property which will be the hash of all supporting models needed within the route. Then in setupController, I apply the results to the auxiliary controller’s content property.

For example, if you have a blog post and want to have all the tags handy to attach to the post:

// routes/post.js
export default Ember.Route.extend({
  auxModels: null,

  model: function(params) {
    this.get('store').find('post', params.post_id);
  },

  afterModel: function() {
    Ember.RSVP.hash({
      tags: this.get('store').find('tag');
    }).then(function(hash) {
      this.set('auxModels', hash);
    });
  },

  setupController: function(controller, model) {
    auxModels = this.get('auxModels');
    controller.set('controllers.tags.content', auxModels.tags);
  }
});

// controllers/post.js
export default Ember.ObjectController.extend({
  needs: ['tags']
});

#5

@doug316, I posted this question on Stack Overflow, and eventually found your posting. Of everything I’ve seen so far, I like this approach the best. 3 months later, is this still the approach you’re using?


#6

Hi Josh,

I would still prefer this approach, with the variation of making it a mixin and using a property for the auxModel types to keep things dry. However I currently have the initial request return all of the resources I need. These all get pushed into the store automatically, you have to then pull them out with this.store.all('post') for example, replacing the individual find requests.


#7

It’s all a bit ugly, isn’t it.


#8

I feel much better about loading everything in the same request. This is one case where a little bit of early optimization would have been better.

I think it’s great that ember-data automatically pushes the sideloaded records into the store, and also valuable that it allows me to choose where to attach references to them. One optimization I’m considering is having it automatically assigning them to the content of the appropriately named controller, if present and not overridden. This way you could create meta-resources and have controllers to manage them, and if the server sends them down, they go right where you need them.


#9

No moar right way meme!


#10

Though yeah, overloading the model hook with multiple models is definitely the worst way to do it. It’s not called the ‘models’ hook. Just start instantiating controllers when you need them and putting the data in there, in the afterModel hook. By that time, you can already call the current model, so no need to make other requests or whatever.


#11

I don’t know that meme, but I wasn’t meaning “THE” right way, just any right ways would be good. LOL.

It does seem a little like there aren’t any approachable descriptions of how to get the framework to do some fairly commonly required things, which I think is mostly the biggest reason for the lack of uptake of ember. It’s very very hard to approach because of that. I think Rails was brilliant at this making things seem approachable… amusingly maybe not so brilliant anymore.

Take learning Apple’s iOS ecosystem by comparison. That is about the same level of complexity, but it’s much more approachable. They have, if anything, too much explanation.


#12

@JulianLeviston how about an Ember Quiz thread? Like, maybe have it once a month or every couple weeks and give out a prize or something like that. This could tackle the problem of having practical examples and bring some energy to the forums.


#13

Winner gets a TOMSTER! http://devswag.com/products/ember-mascot-v2-tomster

You know I initially didn’t like the Tomster but now I pair program with him all the time it’s badass.


#14

Hi Doug, sorry for the delayed response. I just wanted to say thx for your response on this, and also link to the final resolution I decided on: http://stackoverflow.com/a/26131063/2308858.

In my case, I decided to set the models directly on their own properties versus wrapping them in controllers. I notice lots of people like to wrap their models in controllers. Do you know what the benefit of this is?

I can see how if you already have routes that correspond to these controllers and models, it’s nice to get any “decorating properties” that the controller adds, but if you don’t have such routes, I don’t understand what values this adds.

Thx for your feedback!


#15

Glad to be of some help Josh. I see your solution differs mainly in that you reference the controller in afterModel. I don’t recall if when I wrote mine, that version of ember had not yet created the controller instance or what, so I waited for setupController to make the assignments. Or maybe trying to go with the flow. Now, I would say the disadvantage of this approach is that it (and my original example) makes multiple HTTP requests, all the while blocking the UI from rendering. If your case warrants and you want your page to render sooner, you could consider doing individual finds and assigning them to the content controllers, which will render out as empty until the promises resolve, i.e. staged loading.

Ember IMO has a decent amount of black magic, and its hard at times to get definitive answers, let alone “official” ones. I’ve decided the best way is to try to write code quickly that works but is easy to change. Over time I’m picking things up from blog posts, stack overflow, etc. and I can go back and refactor. I’ve found that spending too much time trying to find the “best way” is not always fruitful and the solution will come naturally over time as I learn and as my opinions solidify. Overall this is “avoid premature optimization” while planning for it.

Per your question about using item controllers: I like the notion of keeping models for persistence only. I use item controllers for decoration and any model-centric operations. Then, I may have subclassed itemControllers that extend functionality for the use of those models in different contexts, whether for function or for views. If you display a model in different contexts you may want to override or implement things differently, and some sort of proxy is good for that. Personally I like consistency, so I’ve decided to always use them, even if they end up being simple. In my case, I have a base class that does a lot of boilerplate and this has become pretty useful. Sometimes an itemController is just a class derived from the base and nothing more. If you to this route, you have a place for any common code that you want to apply to the views of all of your models.


#16

I think it’s a huge problem that more beginning coders don’t READ enough before they get started writing, but also, there’s a converse problem in that there isn’t really a great wealth of well written simple actual problems being publicly solved. (ie there isn’t a whole lot TO read).

That’s why I quite like the cookbook approach. The community probably needs a much bigger cookbook, with EVERYONE contributing.


#17

Hi @doug316,

Thanks again for the detailed, thoughtful response!

Actually, in my afterModel hook, it does wait until the model hook has finished rendering, but then the page renders immediately and the properties are filled in asynchronously.

You are spot on with this observation. I’ve spent too much time in the past looking for the official way, and when I change things later, you’re right, it’s always easy to refactor. Yeah, going forward, I am definitely going to timebox myself on “Ember Way” searches.

I appreciate the clarification. Can you give an example of what your base controller class does for you? Also, are you saying that you manually define an item controller for each model? Do you have a folder /app/controllers/models so you never run into conflicts with your “route-based” controllers?

I still can’t see any situations where you actually need to decorate a model that’s not the backing model, and where just adding computed properties to the model itself wouldn’t be good enough.


#18

According to the docs for afterModel:

if the value returned from this hook is a promise, the transition will pause until the transition resolves.

As of now, I handle the following in my base itemController class and via its included mixins, including sharing code with the route classes:

  • Translate model name into default internal strings, i18n keys, and humanized values
  • Validation hooks
  • Page titles
  • Breadcrumbs

Also, I have mixins specific to each REST action type, new, edit, etc. So I would have CommentNewController < CommentEditController < CommentController < ResourceController < Ember.ObjectController. The edit and new controllers add mixins EditControllerMixin and NewControllerMixin for shared REST code for those operations.

My takeaway from this is that I expect apps will need to add shared code on top of models as more features are added, to keep code DRY. There are other ways of course, such as mixing in to the model classes, but I elected to go with this approach. Incidentally, some of the same or variations of the mixins are also used for each models array controllers in a similar way.

One downside is that yes, for every model I have to declare a route and controller for both the model and collection. An upside is that when I need to add a feature, I know right where to put it so that every model can use it.


#19

You don’t have to declare a route if a controller by itself will suffice. Like, let’s say a login route. You can pop an application-wide modal, have a login/session (up to you what you name it) controller, and never have to visit /login. Similar for collections, you can call this.generateController(‘foo’) and never have to instantiate a route if that’s not part of your UX.

Just because the router handlers errors for free doesn’t mean that’s always where you do error handling.


#20

I’m writing this ‘let’s code!’ showing you how to roll out your own JWT-Backed authentication with ember and express if you’d like to read it :sunglasses:

Improving the Cookbook is a good idea, definitely worth doing. If anyone wants someone to read their drafts and give feedback, I’m totally down, just let me know.