1
votes

I'm trying to figure out the "right" way to structure a part of my Ember app

A basic overview:

Let's say I have a Course model defined similarly:

var Course = DS.Model.extend({
    videos: hasMany('video', { async: true }),
    categories: hasMany('category', { async: true })
});

Each video has a category associated with it.

In my CourseView or CourseController, how am I supposed to get a list of the videos for each category?

First, I tried using a computed property, but any call like this.get('model').get('videos') returns a Promise – not what I want.

Next, I saw some posts about using that and then setting properties on the controller after the Promise resolves, so the View would update when the properties changed, but this involves a lot of almost-callback hell and I'm hoping there's a better way to approach the problem.

Is there any way I can "load" the videos/categories before the view gets rendered? Ideally I'd be able to do something like:

{{#each category in categories}}

    // render category-related stuff and only videos in this category

{{/each}}

Would it be better to have all of the videos on the Category? Is there any way I can define/add properties to a model without having them be sent to the server? I guess this can be seen as a general question about API design as well – should videos be under categories? Essentially there'll be a list of Courses, each with its own set of categories. Every video must fall under one category. After typing it out this way it makes sense for videos to be under categories if all of them must be under some category...

Edit: Here's an implementation I just went with for the sake of time:

My Category model now looks something like this:

var Category = DS.Model.extend({
    name: attr('string'),
    description: attr('string'),
    videos: hasMany('video', { async: true })
});

In the Course template:

...

{{#each category in model.categories}}

    {{render "course/category" category}}

{{/each}}

...

In the course/category template:

<h2 class="sub-title">{{model.name}}</h2>

{{render "course/video-gallery" model.videos}}

where course/video-gallery is a something I built for a collection of Videos.

This works fine, but now there are a bunch of Video models everywhere; on the Course (all of them) and on each Category (all of them that belong to that Category).

1
Can a video be under multiple categories? - Oren Hizkiya
Is there a probability of a video having more than one category? - Hrishi
You could also look at side loading videos - urls to the videos I'm assuming. - Hrishi
Right now there's no need/it doesn't fit with what I'm trying to build, but later on I'll probably realize that's a dumb idea so let's say yes - Dennis
I just implemented a solution where Categories have Videos associated with them (I didn't want to do this initially because I was afraid of all of the network requests the app would be making but it doesn't seem to be making them again after initially loading them for the Course) - Dennis

1 Answers

2
votes

I don't know your specific use-case, but if I was designing something similar, I would have my video class have a hasMany('categories') relationship, expressing the real world notion that a video can be "tagged" with multiple categories, then the models would look something like this:

var Course = DS.Model.extend({
  videos: hasMany('video', { async: true }),
  categories: hasMany('category', { async: true })
});

var Video = DS.Model.extend({
   categories: hasMany('category'),
    //rest of the attributes
});

var Category = DS.Model.extend({
   name: DS.attr(),
   description: DS.attr(),
   //other attributes
});

now, you need a list of videos for each category, to break it down into a simpler problem, lets imagine you can get all the videos for one category. That's easy enough, all we need to do is get the list of videos and select those that have this category's id in their categories list. So the resulting code in ember, if made a computed property on the controller would look like this:

videosByCategory: function() {
   var cats = this.get('categories'), //grab courses' categories.
       videos = this.get('videos');   //grab the courses' videos
  return videos.filter(function(video) {
     var videoCategories = video.get('categories');
     return Ember.EnumerableUtils.intersection(videoCategories,cats).length > 0;
   });
}.property('videos'),

now, in the template, if you access this computed property like this:

{{#each video in videosByCategory}}
  //whatever goes here
{{/each}}

Hope this helps. I would sideload the videos with the course, when downloading the course.