Last Updated: February 25, 2016
·
6.549K
· davert

Ember Data: Alternative Approach to Polymorphic Associations

Ember Data has introduced support for polymorphic associations pretty while ago. Still, it's pretty hard to use it, even with ActiveSerializers gem.

Here is how it should work, for user could have Post/Comment messages.

App.User = DS.Model.extend({
 messages: DS.hasMany(App.Message, {polymorphic: true})
});

App.Message = DS.Model.extend({
  created_at: DS.attr('date'),
  user: DS.belongsTo(App.User)
});

App.Post = App.Message.extend({
  title: DS.attr('string')
});

App.Comment = App.Message.extend({
  body: DS.attr('string'),
  message: DS.belongsTo(App.Message, {polymorphic: true})
});

The one problem I got was declaring this lines:

DS.RESTAdapter.configure('App.Post' {
  alias: 'post'
});
DS.RESTAdapter.configure('App.Comment' {
  alias: 'comment'
});

When posts and comments are requested by other then model that has polymorphic association, a naming conflict occur and I got "post already declared" failed assertion.

On other hand, do we really need a thing like polymorphism on client side?

I decieded to try data in traditional way, even my server-side association was declared as polymorphic:

App.User = DS.Model.extend({
 posts: DS.hasMany(App.Post),
 comments: DS.hasMany(App.Comment)
});

The hardest thing here is to get ActiveSerializers to create side loading for polymorphic association in a model. I decided to manually insert comments/posts if owner model have them.
Here is a snippet to use in my UserSerializer class from ActiveModelSerialzers:
Let's say we have belongs_to :message, polymorphic: true in User class.

# what kind is polymorph?
def message?(model)
  object.message.class.name == model
end

# dynamically creating side loaded associations  
['Post', 'Comment'].each do |model|
  downcased = model.downcase

  has_one downcased.to_sym, embed: :ids, include: true

  define_method downcased do
    object.message if message?(model)  
  end

  define_method "include_#{downcased}?" do
    message?(model)  
  end

  define_method "#{downcased}_id" do
    object.message_id
  end

  define_method "include_#{downcased}_id?" do
    message?(model)
  end    
end

If model's associate is comment we append comment into json.
If it's post we append post.

Personally, I find this even easier to use on client side.
Hope that idea might help someone

3 Responses
Add your response

When using active model serializers I found it was capitalising the _type columns and setting the alias to the capitalised singular avoided the "x already declared" errors.

over 1 year ago ·

Yep, might have worked too. But for my project, this hook worked better. Because I didn't require parent class for my associated models.

over 1 year ago ·

When defining the hasone you should add root: model.underscore.tosym to support camelcase model names.

over 1 year ago ·