Polymorphism and Backbone.js Collections
There are probably hundreds of ways to do this, and if you're reading this and happen to know a better way, I'd love to hear it. But I'm pretty happy with what I came up with.
The Problem:
I'm working on a social github-related app where users can create "boards" where they can share their github projects. I realized for different areas of the application, I would sometimes want an index of a particular user's projects, and other times, I would want all the projects belonging to a board. For this reason I couldn't make projects nested in my api routes, so what to do.
Solution Part I
Pass additional options as parameters to the projects controller when you send a get request. This looks like this:
var projects = new ProjectBoard.Collections.Projects(); projects.fetch( { data: { user_id: 1 } } );
Fetch takes a data object as an option, which itself will take an object and inject it into your query like so:
http://localhost:3000/api/projects?user_id=1
The nice thing about this is you don't have to get hacky with the url property of the backbone collection.
Solution Part II
Listen for those additional params in on the controller serving the index. Then you can cleanly use associative collections of the appropriate models, in this case Users and Boards both have many projects.
def index if (params[:user_id]) @projects = User.find(params[:user_id]).projects elsif (params[:board_id]) @projects = Board.find(params[:board_id]).projects else @projects = Project.includes(:author, :tags).all end render json: @projects end
Solution Part III
Override the Backbone.Collection.fetch() method to check for this._boardId or this._userId, and if so, inject that data into the get request. Calling super on your backbone classes turned out to be pretty easy, I was happy with how clean this code came out, for doing something so useful.
ProjectBoard.Collections.Projects = Backbone.Collection.extend({ url: 'api/projects', ... fetch: function() { var fetchArg = {}; if (this._boardId) fetchArg['data'] = { board_id: this._boardId }; if (this._userId) fetchArg['data'] = { user_id: this._userId }; ProjectBoard.Collections.Projects.__super__.fetch.call(this, fetchArg); } ... });
Summary
Using this method I was able to make my collection "polymorphic". If you initialize it with a board_id, it will always fetch that board's projects. Same with a user_id. And with no arguments, it will fetch all projects from the projects#index as you'd normally expect.












