1

I have these models:

Gmcontrolpanel.Offer = DS.Model.extend({
    name: DS.attr('string'),
    date: DS.attr('string'),
    duration: DS.attr('number'),

    products: DS.hasMany('product', {async: true}),
});

Gmcontrolpanel.Product = DS.Model.extend({
    name: DS.attr('string'),
    description: DS.attr('string'),

    offer: DS.belongsTo('offer'),
    variations: DS.hasMany('variation', {async: true})
});

Gmcontrolpanel.Variation = DS.Model.extend({
    description: DS.attr('string'),
    quantity: DS.attr('number'),
    price: DS.attr('string'),

    product: DS.belongsTo('product')
});

I'm trying to build a reusable interface for creating/editing an offer; i've made two separate views for inserting a product and inserting a variation;

the product view has a + and a - button to add or remove products, and the same for variation view;

the model for the route is:

model: function() {
    return this.get('store').createRecord('offer');
}

What I want is that, when clicking on save button, all (offer, products and variations) are saved;

First of all: which one is the best way of implementing this? containerViews? collectionViews or {{#each}} loops?

And then, how can I create the child records and bind them to the input fields on the child views? I mean: I can create a new product record every time a productView is inserted and the same for variations, but when saving how can I get all these records and set properly all the relationships fields?

Cereal Killer
  • 3,387
  • 10
  • 48
  • 80

2 Answers2

1

Here is a skeleton example of how to set up the relationship:

var newOffer= store.createRecord('offer', {name:....});
//or you can do newOffer.set('name',...);

var newVariation = store.createRecord('variation', {description:.....});

var newProduct = store.createRecord('product', {name:..., description:.....});

newProduct.get('variations').pushObject(newVariation);

newOffer.get('products').pushObject(newProduct);

But for saving the model and persisting it in db, there is one slight problem. Saves are per model, so even when you have the relationship set up properly when we do save on offer model, it doesnot embed the data associated with hasMany relationed models. So we could do something like this :

Note: I have read about bulk save but haven't tried it yet - you might want to give it a shot but if that didnt work then i would do save on each model from bottom up like

newVariation.save().then(function(){
    newProduct.get('variations').pushObject(newVariation);
    //since the variation model is already saved, it has id associated with the model
    //so ember data now knows that it should set variations as variations:[id of the variation model we just saved] when sending post request for product
    newProduct.save().then{ //same pattern as above }
}, function(){//handle failure}

Here the case was simple, we had just one variation and one product but you may have multiple of them. We can do rsvp.all to sync up the promises for saves but it is bit sluggish becuse you have to make separate api calls for each save and since you may have multiple variations and products, the no of ajax calls can be bit insane. One way of getting around this would be that you create your own json structure by looping through the models, and combine the models into single json and make a single post request with jQuery ajax api call, save the content in your db and then make use of pushPayload( http://emberjs.com/api/data/classes/DS.Store.html#method_pushPayload) to load up all the data back to the store.

This is what i have done in similar situation but there might be more elgant solutions out there, so i would wait on more opinions on this.

As for the view thing, i would think you would need a view for product only, this is what im thinking:

//offer.hbs
Bunch of form elemnts to update name description
+ - buttons to add product
{{#each product}}
    {{#view productView product=this}}//this is just a normal view
{{/each}}

// your template associated with productView will be like

+ - buttons to add variations to product
{{#each product.variations}}
   Show form elments to update name and description
{{/each}}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Deewendra Shrestha
  • 2,313
  • 1
  • 23
  • 53
  • Ok, thank you for this; it is helping; how about the view: what do you think it is best in this situation? I mean, since it is needed to display many productsView inside the main view and many variationsView inside any productsView, what's the best choice? Push them in containerViews(one on the main view and one on every productsView); or push them in collectionViews (what's the difference between a container and a collectionview?), or something else... – Cereal Killer Apr 21 '14 at 22:25
  • I have updated my answer about what i think would be the usage of the view. I have not used collectionView but have worked a bit with containerView and collectionView seems to exten container views anyways, but in your situation a normal view should be sufficient. – Deewendra Shrestha Apr 21 '14 at 22:44
  • Thank you for help, I've accepted your answer since it is driving me in the right direction; expecially the way of pushing child record into parent's relationship is a step I was not able to find in docs... – Cereal Killer Apr 21 '14 at 22:53
  • There's a problem with saving; if you save bottom up, starting from variations, you can set the product field on the variation (the belongsTo relationship) because the products does not exists – Cereal Killer Apr 22 '14 at 20:33
  • Well i guess in that case, you would save variation first without setting the product, then you will save products and after they are saved you will re-update the variation model And save them again. Cant you do without belongsTo relationship? – Deewendra Shrestha Apr 22 '14 at 23:58
  • No, belongsTo relationShip is needed for db integrity; I've solved this loading all child records in a local array before saving the model; then, after saving the model, with a forEach loop i save child records taking them from the local array... – Cereal Killer Apr 23 '14 at 00:00
  • but in any case I don't like very much this solution, just because I can't believe there isn't a more elegant solution... – Cereal Killer Apr 23 '14 at 00:03
  • You should probably un-accept my answer, because even I would be curious on how people solve this problem. The only elegant work around that i know of is like i mentioned in my post, combining all the required models into a single json and making ajax call to an api end point which understands this structure and persists it in db and returns the json back as ember-data needs it to be, and load th data with pushPayload. – Deewendra Shrestha Apr 23 '14 at 00:10
  • Yes, I was reading about this method; it's a good solution and it reduces the number of http POST requests; the thing is that I think Ember should have some kind of automatism, some kind of way of managing this... – Cereal Killer Apr 23 '14 at 00:17
0

How about if we give this a try in setupController

setupController:function(controller, model){
    model.reload().then(function(data){controller.set('model', data);});
}

Or, what if you create an action to transition to edit mode and in that action you reload the model first and within its then hook, you do the transitionToRoute with reloaded model data. Something like:

goToEdit: function(model){
    model.reload().then(function(data){transitionToRoute('offer.edit', data});
}
Deewendra Shrestha
  • 2,313
  • 1
  • 23
  • 53
  • This gives errors while loading the route; probably because, unlike what happens when executing model hook, the setupController hook doesn't stop execution of following code, or it is executed too late... In any case I can see that code in the view is executed before the model reload is done and so when trying to access model, it causes the error – Cereal Killer Apr 23 '14 at 00:28