Skip to content

Instantly share code, notes, and snippets.

@riseshia
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save riseshia/2146495129e594c04e32 to your computer and use it in GitHub Desktop.
Save riseshia/2146495129e594c04e32 to your computer and use it in GitHub Desktop.

Backbone.js handsOn

  • Ruby : 2.2.0
  • Rails : 4.1.7
  • backbone-on-rails : 1.1.2.0
  • jQuery : 1.11.2

Preparing

Create App

rails new backbone_app -d sqlite3 --javascript-engine coffee

Add backbone-on-rails gem

cd backbone_app
echo "gem 'backbone-on-rails'" >> Gemfile
bundle install

DB Setup

rails g scaffold task name:string
rake db:migrate

Let's make some data.

  10.times { |i| Task.create(name:"title #{i}") }

Delete unnecessary View

rm -f app/views/tasks/*
touch app/views/tasks/index.html.erb

Setup Front side

rails g backbone:install
mv app/assets/javascripts/backbone_app.js.coffee app/assets/javascripts/todo_list.js.coffee

Change application.js

//= require backbone_app -> //= require todo_list

Change todo_list.js.coffee

window.TodoList =
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}
  initialize: -> alert 'Hello from Backbone!'

$(document).ready ->
  TodoList.initialize()

Run & Inspecter

rails s
  1. open in browser (Optional)
  2. Get Inspecter

Configure Backbone.js

1. Make task scaffold

rails g backbone:scaffold task

2. Router

app/assets/javascripts/routers/tasks_router.js.coffee

class TodoList.Routers.Tasks extends Backbone.Router
  routes:
    '': 'index'

  index: ->
    tasks = new TodoList.Collections.Tasks
    new TodoList.Views.TasksIndex collection: tasks
    tasks.fetch({reset: true})

3. Collection & Model

app/assets/javascripts/collections/tasks.js.coffee

class TodoList.Collections.Tasks extends Backbone.Collection
  url: 'tasks'
  model: TodoList.Models.Task

app/assets/javascripts/models/task.js.coffee

class TodoList.Models.Task extends Backbone.Model

Setup rails

0. setup root route

app/config/routes.rb

...
  root 'tasks#index'
...

1. enable json

app/controllers/tasks_controller.rb

  def index
    render json: Task.all
  end
...
  def create
    @task = Task.new(task_params)

    if @task.save
      render json: @task, status: :created
    else
      render json: @task.errors, status: :unprocessable_entity
    end
  end

2. Find a Spot to render

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
<head>
  <title>BackboneApp</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>
<div id="app">
  <%= yield %>
</div>
</body>
</html>

View

1. TasksIndex

app/assets/javascripts/views/tasks/tasks_index.js.coffee

class TodoList.Views.TasksIndex extends Backbone.View
  el: '#app'
  template: JST['tasks/index']
  events: ->
    'keypress #add-task' : 'createOnEnter'
  initialize: ->
    @collection.bind 'reset', @render, @
    @collection.bind 'add', @addTask, @
  render: ->
    $(@el).html(@template())

    @collection.each (task) ->
      view = new TodoList.Views.TaskItem model: task
      @$('#tasks').append(view.render().el)
    @

app/assets/templates/tasks/index.jst.eco

<div id="tasks">
</div>

2. TasksItem

app/assets/javascripts/views/tasks/tasks_item.js.coffee

class TodoList.Views.TaskItem extends Backbone.View
  template: JST['tasks/item']
  render: ->
    $(@el).html(@template(task: @model))
    @

app/assets/templates/tasks/item.jst.eco

<div><%= @task.get 'name' %></div>

3. Binding Router

app/assets/javascripts/todo_list.js.coffee

window.TodoList =
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}
  initialize: ->
    new TodoList.Routers.Tasks
    Backbone.history.start()

$(document).ready ->
  TodoList.initialize()

4. Refresh Browzer!

you can see some task list!!

Upgrade your app (add feature add & destroy)

1. TasksIndex

app/assets/javascripts/views/tasks/tasks_index.js.coffee

class TodoList.Views.TasksIndex extends Backbone.View
  el: '#app'
  template: JST['tasks/index']
  events: ->
    'keypress #add-task' : 'createOnEnter'
  initialize: ->
    @collection.bind 'reset', @render, @
    @collection.bind 'add', @addTask, @
  render: ->
    $(@el).html(@template())

    @collection.each (task) ->
      view = new TodoList.Views.TaskItem model: task
      @$('#tasks').append(view.render().el)
    @

app/assets/templates/tasks/index.jst.eco

<label>Add Task:<input id="add-task"/></label>
<div id="tasks">
</div>

2. TasksItem

app/assets/javascripts/views/tasks/tasks_item.js.coffee

class TodoList.Views.TaskItem extends Backbone.View
  template: JST['tasks/item']
  events:
    'click a.remove-task' : 'removeTask'
  initialize: ->
    @model.bind 'destroy', @remove, @
  render: ->
    $(@el).html(@template(task: @model))
    @
  removeTask: ->
    @model.destroy()

app/assets/templates/tasks/item.jst.eco

<div><%= @task.get 'name' %> - <a class="remove-task">remove</a></div>

3. Refresh Browser!

And Try!

Upgrade One more! (add remaining counter)

1. TasksIndex

app/assets/javascripts/views/tasks/tasks_index.js.coffee

class TodoList.Views.TasksIndex extends Backbone.View
  el: '#app'
  template: JST['tasks/index']
  events: ->
    'keypress #add-task' : 'createOnEnter'
  initialize: ->
    @collection.bind 'reset', @render, @
    @collection.bind 'add', @addTask, @
  render: ->
    $(@el).html(@template())

    footerView = new TodoList.Views.Footer collection: @collection
    footerView.render()

    @collection.each (task) ->
      view = new TodoList.Views.TaskItem model: task
      @$('#tasks').append(view.render().el)
    @
  addTask: (task) ->
    view = new TodoList.Views.TaskItem model: task
    @$('#tasks').append(view.render().el)
    @
  createOnEnter: (event)->
    return if event.keyCode != 13
    @collection.create name: @$('#add-task').val()
    @$('#add-task').val('')

app/assets/templates/tasks/index.jst.eco

<label>Add Task:<input id="add-task"/></label>
<div id="tasks">
</div>
<div id="footer"></div>

2. Footer

app/assets/javascripts/views/tasks/footer.js.coffee

class TodoList.Views.Footer extends Backbone.View
  el: '#footer'
  template: JST['tasks/footer']
  initialize: ->
    @collection.bind 'add', @updateRemaining, @
    @collection.bind 'remove', @updateRemaining, @
  render: ->
    remaining = @collection.length
    $(@el).html(@template({remaining: remaining}))
    @
  updateRemaining: ->
    @$('#task-count').text(@collection.length)

app/assets/templates/tasks/footer.jst.eco

<span>Remaining:
  <span id='task-count'><%= @remaining %></span>
</span>

3. Refresh broswer and Try!

Congratulation! You just finish hands-on!

Reference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment