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
Written by Michael Bodnarchuk
Related protips
3 Responses
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.
Yep, might have worked too. But for my project, this hook worked better. Because I didn't require parent class for my associated models.
When defining the hasone you should add root: model.underscore.tosym to support camelcase model names.