Writing Javascript code using MVC framework and rendering templates at client side is the new fancy way of writing web applications. As they say, when Rails is the backend, Emberjs at frontend is a good fit.

The beauty of the architecture along with our excitement has impelled us to dive into this framework. It started with revamping of an eCommerce site by building its version-2 using Emberjs as frontend framework and Rails as an api server. Initially, everything was working fine and we were enjoying our Emberjs learning and implementation, but in the following scenario belongsTo association has become a stumbling block for us.

Before we talk about the obstacle, let us share what tools we are using and how we are trying to integrate Emberjs and Rails.

Tools
For setting up our Emberjs application on a Rails project, we are using the ember-rails gem. It, pretty nicely, takes care of the following-

  • Pre-compiling of handlebars templates when building asset pipeline.
  • Inclusion of development and production copies of Ember, Ember Data and Handlebars.
  • Inclusion of ActiveModel::Serializer for integration with Ember Data.

On the Rails side, we are using the active_model_serializers gem to build the APIs and on the ember side, the adapter being used is ActiveModelAdapter.

Let us also brief about the idea and flow of the application.

Application flow
Like any other eCommerce application, we are having products, variants, orders and lineitems. Products are being loaded on the page asynchronously on page load, ‘the Ember way’. An ajax request is sent to the server using Ember models, server returns the products and variants JSON, products’ objects are created in ember store and get displayed on front-end through handlebars templates.

Here are some of the Ember models:

App.Product = DS.Model.extend({
 // attributes
 name: attr(),
 description: attr(),
 display_price: attr(),
 ...

 // associations
 taxons: DS.hasMany('taxon'),
 variant: hasOne('variant'),
 ...
})

 

App.Variant = DS.Model.extend({
  // attributes
  isMaster: attr(),
  name: attr(),
  ...

  // associations
  lineItems: DS.hasMany('lineItem'),
  product: DS.belongsTo('product')
})

 

App.LineItem = DS.Model.extend({
  // attributes
  quantity: attr(),
  price: attr(),
  ...

  // associations
  variant: DS.belongsTo('variant'),
  cart: DS.belongsTo('cart')
})

JSON response for loading Products, Variants and LineItems on page load is attached at the end of this post.

So far, everything is in place and works pretty well. Now, since we have our products on the page, it’s time to start shopping by building the cart.

To add a variant in the cart, we have an api endpoint which accepts variant_id and quantity of the variant as parameters and responds with the json which comprises of the whole Cart containing all line items. For example: if we already have an item in the cart and request to add another item, in the response to the api call, we’ll get both the items.
When the response of add to cart is received, objects of lineItems are created in ember store. And now, we need to display these lineItems in the cart section of the page. Basically, we want to display the variant’s name along with its quantity and price.

For this, we can use the associations between LineItem and Variant models. At the code level, we should say that we have lineitem which has variant_id and variant already exists in the Ember store, so we can use following code to get the associated variant if we have the lineitem object:

lineItem.get('variant')

But, this actually did not work in our case. To our understanding, since lineitem belongsTo variant, this should have worked well.

Let’s have a look at the Ember inspector-

ember_store_screenshot
Currently, just to move ahead, we are manually associating lineitem with related variant by lineitem.set('variant', variant) Here is the snippet:

var _this = this;
// unloading existing lineitems from store since all lineitems are returned in the response of add to cart api call.
this.store.unloadAll('lineItem');
response.line_items.map(function(line_item) {
  var li = _this.store.createRecord('lineItem', line_item);
  _this.store.find('variant', li.get('variant_id')).then(function(variant)  {
    // explicitly associating variant with lineitem
    li.set('variant', variant);
  });
});

What made us to do this blog post is no response to our question on stackoverflow and discuss.emberjs.com

Ajax response
JSON response while loading the products and variants.

products: [
{
  id: 1821,
  name: "Almayass- delight",
  provider_id: 631,
  variant_id: 1811,
]
variants: [
{
 id: 1811,
 name: "Almayass- delight",
 price: "15.0",
 is_master: true,
 permalink: "almayass-delight",
 description: "BY VESELKA in EAST VILLAGE"
},
{
 id: 2471,
 name: "Almayass- veggie",
 price: "18.0",
 is_master: true,
 permalink: "almayass-veggie",
 description: "BY VESELKA in EAST VILLAGE"
}]

Also, when we send POST request to add lineItem, we get the whole cart in response. The response is as follows-


{
line_items: [
  {
    id:60615,
    quantity:2,
    price:"20.0",
    variant_id:2471,
    product_name:"abc",
  },
  {
    id:60614,
    quantity:1,
    price:"15.0",
    variant_id:1811,
    product_name:"Almayass- delight",
  }
 ]
}

We would be more than happy to know where we went wrong. Feel free to put your suggestions in comments.

Share this: