1
votes

I´m pretty new to ember development and need help in handling this kind of task:

Currently I am working with Fixtures in an ember-cli app. The two models concerned are:

var Recipe = DS.Model.extend({
   title: DS.attr('string'),
   body: DS.attr('string'),
   ingredients: DS.hasMany('ingredients',{async: true}),
   recipeCategory: DS.belongsTo('recipeCategory', {async: true})
});

var Ingredient = DS.Model.extend({
   title: DS.attr('string'),
   portion: DS.attr('string'),
   groupTag: DS.attr('string'),
   recipe: DS.belongsTo('recipe')
});

While there are no problems in listing all ingredients - also sorted - for a specific recipe called via nested routes,

this.resource('recipes',function(){
   this.resource('recipe', {path: '/:recipe_id'});
});

I am encountering big problems while grouping ingredients by groupTag. The logic for grouping is not the problem, but I either run into race conditions accessing the models in controller for computed properties or getting framework errors when trying to handle promises in templates.

Here are the concerned templates:

//recipe.hbs
<strong>{{recipeCategory.title}}</strong>
<h3>{{title}}</h3>
{{render 'ingredients' ingredients}}

//ingredients.hbs
<strong>Zutaten</strong>
<ul>
    {{#each groupedIngredients}}
      <li>{{group}}
        <ul>
            {{#each items}}
                <li>{{portion}} {{title}}</li>
            {{/each}}
        </ul>
      </li>
    {{/each}}
</ul>

My Ingredients-Controller looks like this:

var IngredientsController = Ember.ArrayController.extend({
   sortProperties: ['title'],
   sortedIngredients: Ember.computed.sort('model', 'sortProperties'),
   groupedIngredients: function(){
       return this.get('model').then(function(ingredients){
          var groupTags = ingredients.mapBy('groupTag').uniq();
          var groupedIngredients = groupTags.map(function(gtag){
            return {
                group: gtag,
                items: ingredients.map(function(item){
                  if ( item.get('groupTag') == gtag){
                    return item;
                  }
                }).compact()
             };
         });
        console.log(groupedIngredients);
        return groupedIngredients;
      });
  }.property('model')
});

The console log inside the promise is fine, but I can not return the promise for evaluation to the template:

Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed {_id: 158, _label: undefined, _state: undefined, _result: undefined, _subscribers: } 

When I remove the promise and just work on this.get('model'), the computed array is full of undefined values, cause the model seems not to be fully loaded. How can I fix this issue to work on async model data in this way? Thanks!

2

2 Answers

0
votes

You don't need to do your computation in a then hanging off of get('model'). By the time you have reached this point in your code, the model is already resolved and ready to go. The router has already ensured that the model promise is resolved before proceeding.

Therefore:

groupedIngredients: function(){
    var ingredients = this.get('model');
    var groupTags = ingredients.mapBy('groupTag').uniq();
    var groupedIngredients = groupTags.map(function(gtag){
        return {
            group: gtag,
            items: ingredients.map(function(item){
                if ( item.get('groupTag') == gtag){
                    return item;
                }
            }).compact()
        };
   });
   console.log(groupedIngredients);
   return groupedIngredients;
}.property('@each.groupTag')

To avoid having to do compact, just switch to using filter:

items: ingredients.filter(function(item){
    return item.get('groupTag') === gtag;
}

which is the same as

items: ingredients.filterBy('groupTag', gtag)

Here's an implementation of groupBy as a computed property, which you might be able to adapt, and if it works would let you simply do

groupedIngredients: Ember.computed.groupBy('groupTag')
0
votes

I had similar issues with my code and it usually dealt with setting the wrong dependency for the computed property.

Based on your code I would say your groupedIngredients: property should probably be along the lines of:

.property('@each.groupTag')

Once set correctly, you should be able to remove the promises from your controller, since it should automatically update once the promise is fulfilled.