Backbone is becoming one of my favorites tools to work with. I use it every chance I get to. However, one of the obstacles I’ve found by using it is the use of associations in my models.
Coming from a Rails scenario, I want to have my rails associations reflected on my backbone models. An association implies to tie up two or more models in a strong way. Keeping two models tied in backbone is a bit harder and needs to be done manually.
Days ago, when I was attending Backboneconf, one of the speakers (Rebecca Murphey) said that Backbone is like raw stones, and that it is our responsibility to take advantage of that stone and build something with it. In brief, we should extend backbone. In her talk, Rebecca mentioned Supermodel.js, a Backbone model extension with a lot of extra functionality, one of these is model associations.
Next, I’ll show you how to create a simple Rails/Backbone app using Supermodel.js.
- Create a new Rails app.
rails new supermodel
- Create 2 resources (Company and Product), the association will be 1-n.
rails g resource Company name:string rails g resource Product name:string company_id:integer # # company.rb # class Company < ActiveRecord::Base attr_accessible :name has_many :products end # # product.rb # class Product < ActiveRecord::Base attr_accessible :company_id, :name belongs_to :company end # # application_controller.rb # class ApplicationController < ActionController::Base protect_from_forgery respond_to :json end # # companies_controller.rb # class CompaniesController < ApplicationController before_filter :find_company, except: [:index, :create] def index respond_with Company.all, include: :products end def show respond_with @company, include: :products end def create @company = Company.new params[:company] @company.save respond_with @company, include: :products end def update @company.update_attributes params[:company] respond_with @company, include: :products end def destroy @company.destroy respond_with @company, include: :products end private def find_company @company = Company.find params[:id] end end # # products_controller.rb # class ProductsController < ApplicationController before_filter :find_company before_filter :find_product, except: [:index, :create] def index respond_with @company.products, location: company_products_path(@company) end def show respond_with @product, location: company_products_path(@company) end def create @product = @company.products.new params[:product] @product.save respond_with @product, location: company_products_path(@company) end def update @product.update_attributes params[:product] respond_with @product, location: company_products_path(@company) end def destroy @product.destroy respond_with @product, location: company_products_path(@company) end private def find_company @company = Company.find params[:company_id] end def find_product @product = @company.products.find params[:id] end end
- Create a home controller with and index action (I will use this controller to play with the javascript console).
rails g controller home index
- Setup our routes.
resources :companies do resources :products end root :to => 'home#index'
- Add this to our project, I’ll use the rails-backbone gem.
# # Gemfile # gem 'rails-backbone' bundle install rails g backbone:install
- Download the Supermodel extension and place it in vendor/assets/javascript, then require it in your application.js file.
// // application.js // //= require jquery //= require jquery_ujs //= require underscore //= require backbone //= require supermodel //= require backbone_rails_sync //= require backbone_datalink //= require backbone/supermodel-example //= require_tree .
- Create a company model using Supermodel
# # company.js.coffee # class App.Models.Company extends Supermodel.Model urlRoot: '/companies'
- Create a product model using supermodel, then create the products collection
# # product.js.coffee # class App.Models.Product extends Supermodel.Model class App.Collections.Products extends Backbone.Collection model: App.Models.Product
- Create a file for our associations
# # associations.js.coffee # App.Models.Product.has().one 'company', model: App.Models.Company inverse: 'products' App.Models.Company.has().many 'products', collection: App.Collections.Products.extend url: -> "/companies/#{@owner.id}/products" inverse: 'company'
I used a separate file because our models could not be loaded when we are defining our associations, we just require this file after our model files.
# # supermodel.js.coffee # #= require_self #= require_tree ./templates #= require_tree ./models #= require ./associations #= require_tree ./views #= require_tree ./routers window.App = Models: {} Collections: {} Routers: {} Views: {}
- And that is all the code, now we just need to start the application and play with the javascript console.
bundle exec rake db:create bunde exec rake db:migrate rails server
Go to http://localhost:3000/ in your browser.
Now you can try the following lines in the browser’s javascript console.
company = new App.Models.Company({ name: 'foo' })
company.save()
company.products().create({ name: 'product 1' })
company.products().create({ name: 'product 2' })
company.products().toJSON()
// you'll see an array containing the products created above
company.products().first().company() === company // true :)
Associations is only one of the numerous features of Supermodel.js if you want to know what else you can do with it, read its documentation.