Obtaining related (Ember) data within a component


#1

Hi,

I have a model with a many to many relationship eg products and categories.

In my category model I have this

export default DS.Model.extend({
  name: DS.attr('string'),
  products:DS.hasMany('product')
});

In my category template I can view the related products eg

{{#each model.data.products as |item|}}
{{item.name}}
{{/each}}

What I want to do is obtain the data within a child route for using within a component.

In my child route (item), I can grab the model for the parent route (items) which gives me the product categories (and I assume also) the related products for each of those categories…

model(params) {
  let model = this.modelFor('product.items')
}

However, I’m struggling to obtain the data so I can pass it to a component within the model hook… I thought I could do something like model.data.get(‘products’) but that’s not correct.

Many thanks in advance.


#2

your model booble in child routes,

/categories
    model() { ... }
    access {{model}}
/categories/index
    already has {{model}} from parent

So I think you can link it to your component whithin inner route.

/categories/index
{{#each model.data.products as |item|}}
    {{my-component item=item }}
{{/each}}

Give more detail to try help you.


#3

Hi,

Thanks for getting back.

I can access within a template no problem - I need access to the related item data within the model hook so I can pass that data to a component - sorry if that wasn’t clear.

Assuming the route for the category page is product.items - I have that route backed by the the category model, which has access to the related items (products).

When I get to the product detail route eg product.items.item I have access to the item itself no problem, but I also want access to other related items in the category you are viewing. I need that in the model/component and I can’t seem to access that data… The data is being passed down to the child route but I don’t know how to access it this way (rather than via a template)

Hope that helps!


#4

Hi,

Is this not possible to do?

I’m not sure why I can’t access the related data in a component for example, having passed the parent model.

The parent model (in the product.items route) returns a category object which contains related product info via a many to many relationship.

In my child route, I’m obtaining that model via the modelFor(‘product.items’) method

I can access the data for that parent model in my component eg

let parentModel = this.modelFor('product.items');
console.log(parentModel.get('name')); // logs the correct name of the parent category

However, I can’t access the related data - which from the model definition above is ‘products’ … I thought I could do

let products = parentModel.get('products')

This doesn’t work - does anyone know what I am doing wrong here?


#5

Hi,

The reason I wasn’t getting the expected results was that the app was refreshing on the product detail route (product.items.item) each time and when that happens, the length of the products array is always zero.

If I arrive at the same child route without an app refresh, I do get the expected results. Now just got to try and find out why this is happening! I can still access the category name as shown above when the app refreshes on the child route…

Any help on why this is happening would be great!


#6

I had a similar issue with my code. It turned out that sometimes get('blah') was returning a promise because the data I was trying to get was not already loaded. Try treating your get like a promise and see if that helps.

Alternately, you could try side-loading the products data when you get your category/categories. This may alleviate the need for Ember to asynchronously go get the data since it’s already in your app’s data store.

This is also why your template works but not your route. The templates know how to deal with the promise, while your route is treating it like a synchronous call and thus has nothing at the time of execution.


#7

Hi,

Many thanks for getting back. I’ll give that a go!

Have been thinking about side-loading the data, but will see if I can resolve it without resort to that.

Many thanks for you help.


#8

Hi,

Having some (no doubt basic!) problems in treating the get like a promise in my component …I’m not able to return anything sensible from the promise (apart from length) and to use that

I want to do something like

let relatedItems = mainModel.data.get('products').then(answer => {
    // how to return my objects from this ?
},reject => {
  console.log('errr');
});

The mainModel here is obtained by the modelFor hook in the product.items.item route.

I’m sure it’s basic, but it’s not playing well for me I’m afraid ! Can you give some guidance?

Many thanks!


#9

Whatever you were going to do with the products (i.e. relatedItems), you would instead do inside the then.

mainModel.data.get('products').then(relatedItems => {
  //Work with related items here.
}, reject => {
  console.log('error: ' + reject);
});

If this doesn’t help, maybe you could elaborate on what you are trying to do with products in your code.


#10

Hi,

Thanks for getting back - this is the code I would use. However, aside from getting the length of the relatedItems array, I’m struggling to get anything meaningful. When I get some time, I’ll provide some more info.

Many thanks


#11

Hi, Got it working now … just doing something like

mainModel.data.get(this.get('relatedModel')).then(relatedItems => {
  relatedItems.forEach(function(item,index) {
     // do something with item object 
});
},reject => {
   console.log('error: '+reject);
});

Not sure what exactly I was doing wrong before!

Many thanks for your feedback - much appreciated


#12

Hi,

Even if I create a simple object like this where model is the category model

 export default Ember.Component.extend({
    pagination:null,
    testing:null,
    willRender() {
        let page = {};
        model.data.get('products').then(relatedItems => {
            page.name = 'hello world';
            this.set('pagination',page);
            this.set('testing','this is a test');
           
            console.log(this.get('pagination')); // prints object ok
            console.log(this.get('testing')); // prints this is a test

        },reject => {
          //as before 
        });
      }
    });

In my template if print out {{testing}} I get the correct result However, if I try and use pagination eg {{log pagination}} the browser crashes, and in the console I just get the object printed in a loop.

Do you know why this is happening? Many thanks in advance!


#13

Hi, I’m going to take a different approach here - clearly need to rethink the component !!!