Step-by-step “Hello World” examples in JSON, Eco, and Backbone.js
Moving along from Part 2 of this 3-part “Hello World” mini-series within our our web and mobile development tutorials, here we'll walk through JavaScript Object Notation (JSON), embedded CoffeeScript (Eco), and Backbone.js. If you have not yet completed steps 1–7 from Part 1 and steps 8–10 from Part 2, you should go back and do so now. If you have, great, let's continue:
[Author's note: I wrote the first couple dozen tutorials in this series a few years ago and I'm in the process of updating the content to reflect the evolution of best practices in the industry. As I mentioned previously, Eco and Backbone aren't widely used any longer, but are still useful for a beginner to learn the basics of JS templating and JS frameworks. Thanks!]
STEP 11: “HELLO WORLD” IN JSON
As you hopefully recall from our XML and JSON intro post, JSON (JavaScript Object Notation) is a standardized format to pass data around and/or store it. For our purposes here, we'll use Rails' handy .to_json method to create a JSON object and then print from it using JavaScript into a new paragraph tag in our index.html.erb file:
<p id="p_HTML">Hello World</p>
<p id="p_CSS"></p>
<p id="p_JavaScript">
<script type="text/javascript">
document.write("Hello World");
</script>
<p id="p_jQuery"></p>
<script type="text/javascript">
$(document).ready(function(){
$("#p_jQuery").text("Hello World")
});
</script>
<p id="p_CoffeeScript"></p>
<% user = User.first %>
<p id="p_Rails"><%= user.first_name %> <%= user.last_name %> </p>
<p id="p_JSON"></p>
<script type="text/javascript">
$(document).ready(function(){
var user = <%=raw user.to_json %>;
$("#p_JSON").text(user["first_name"] + " " + user["last_name"]);
});
</script>
Within our new document.ready() method we first set the JavaScript variable “user” to be the JSON object represented by the string returned from the .to_json method on our Rails variable (you can view source and see exactly what that is).
You may have noticed that we added a <%=raw for that output, this is needed because the default <%= automatically escapes characters (which is a safety feature to prevent your site from getting hacked, but messes up our JavaScript).
Once that variable is set with our JSON object then we can access user[“first_name”] and user[“last_name”] like we did to output Hello World. This sets us up nicely for how we'll use Backbone.js
STEP 12: “HELLO WORLD” IN ECO AND BACKBONE.JS
First we're going to install the backbone-on-rails gem. Open up your “#{Rails.root}/Gemfile” and insert this around line 9 or so on its own line:
gem 'backbone-on-rails'
Then go back to your terminal, ctl-c out of your server that is loaded, and:
~/apps/OurAgendaApp $ bundle install
. . .
~/apps/OurAgendaApp $ rails g backbone:install
. . .
~/apps/OurAgendaApp $ rails g backbone:scaffold User
. . .
These commands install the gem, sets up Backbone.js in the directory structure that we'll be using, and writes a few scaffold files for our user model. Yep, you're going to have to get used to a “Rails model” and a “Backbone model” — our Rails models will pass back and forth to/from our Backbone models via JSON.
At this point we need to tweak the order that our JavaScript files are loaded in the browser in order for Backbone.js to work properly. Open up “%{Rails.root}/app/assets/javascripts/application.js” and remove this line:
// require_tree .
…and instead add this line at the bottom of the file, 1 line under the .//routers line:
// require_directory .
The order that these lines appear is critical, otherwise you will run into a bunch of “undefined” errors.
OK, now this is where it gets fun. Open up that “#{Rails.root}/app/assets/javascripts/our_agenda_app.js.coffee” file and tweak it to look like this:
window.OurAgendaApp =
Models: {}
Collections: {}
Views: {}
Routers: {}
init: ->
OurAgendaApp.usersCollection = new OurAgendaApp.Collections.Users()
OurAgendaApp.usersIndexView = new OurAgendaApp.Views.UsersIndex()
OurAgendaApp.usersRouter = new OurAgendaApp.Routers.Users()
Backbone.history.start(pushState: true)
$(document).ready ->
OurAgendaApp.init()
This file defines our Backbone app, which is wrapped up completely in the OurAgendaApp JavaScript object. In order for it to work, we need to fill in some code in our collection, view and router files.
Open up first the “#{Rails.root}/app/assets/javascripts/collections/users.js.coffee” file and add in this third line:
class OurAgendaApp.Collections.Users extends Backbone.Collection
model: OurAgendaApp.Models.User
url: '/api/users'
That line tells backbone where to fetch our index of users, so let's open up our “#{Rails.root}/config/routes.rb” file and add this around line 48 or so:
. . .
scope 'api' do
resources :users
end
. . .
Then let's quickly setup our users controller with an index action to return JSON for all attributes of all the users in our database. Create the file “#{Rails.root}/app/controllers/users_controller.rb” and add these lines:
class UsersController < ApplicationController
respond_to :json
def index
render :json => User.all.to_json
end
end
Cool. You can browse to localhost:3000/api/users in your browser to see the output that our backbone.js app will grab in the background (via AJAX).
Now let's open up our Backbone users index view file at “#{Rails.root}/app/assets/javascripts/views/users/index.js.coffee” and set it up with these 15 lines to keep track of (and output) our users to the browser when we call its render() method:
class OurAgendaApp.Views.UsersIndex extends Backbone.View
initialize: ->
_.bindAll(this, 'add')
this.on "reset", => this.reset_collection()
render: ->
this.reset_collection()
$("body").append(this.$el)
reset_collection: ->
_(@userViews).each (uv) -> uv.off(); uv.remove()
@userViews = []
_(OurAgendaApp.usersCollection.models).each(this.add)
add: (_model) ->
userView = new OurAgendaApp.Views.UsersShow(model: (_model))
this.userViews.push(userView)
this.$el.append(userView.render().$el)
Ok, ‘some crazy stuff going on here so let's discuss method by method:
- initialize() — kicks off when we first create a instance of our “OurAgendaApp.Views.UsersIndex” class (which we called OurAgendaApp.usersIndexView in our our_agenda_app.js.coffee file above). This method binds the infamous and powerful “this” JavaScript declaration so that in our “add” method, “this” will refer to our class object and not our method object (or some deeper internal function). This gets insanely confusing (lol), so you should read more about “this” in the Backbone/bindAll context here for a minute before continuing.
- The second line of initialize() tells our object to fire its reset_collection() method when our collection gets reset after we fetch users from our Rails app (which we'll do below).
- render() — fires our reset_collection() method then outputs our view element to the bottom of our HTML body.
- reset_collection() — calls the backbone off() and remove() methods on each userView we setup, which removes bindings and removes the elements from our browser. Then we create a blank @userViews array and send each of our collection backbone models to our add() method using the handy each() method.
- FYI the “@” in CoffeeScript is another way to write “this.” — and is used conventionally to denote a variable attached to the class object that is used in multiple methods)
- add() — sets up a userView object for the passed model, adds the object to our @userViews array so we can keep track of it, then appends the rendered userView to the index view. Essentially, this method builds our index element out of the individual user views.
OK, we know those 15 lines above are likely massively confusing to you at this point, but let's keep going — it will make more sense later in this tutorial series when you start working with a real app.
You may have noticed above that we haven't actually defined our OurAgendaApp.Views.UsersShow object yet, so let's do that. Create the “#{Rails.root}/app/assets/javascripts/views/users/show.js.coffee” file:
class OurAgendaApp.Views.UsersShow extends Backbone.View
tagName: "p"
template: JST['users/show']
render: ->
$el.html(@template(user: @model))
return this
Here we're declaring that this element will be a
tag and will use an embedded CoffeScript (Eco) template, which gets passed our model, to determine what to put inside. Go ahead and create “#{Rails.root}/app/assets/javascripts/templates/users/show.jst.eco” and put in this one line:
<%- user.get("first_name") %> <%- user.get("last_name") %>
We'll be using Eco templates heavily in our app — they are a convenient way to segment out HTML for different elements of our app and embed data from our Rails/Backbone.js model objects. For now, we just display the simple first and last name of our user without any additional HTML because at this point we're already working inside the
tag we setup in our Backbone view.
Ok — finally — let's setup our Backbone router to kick things off and render our Hello World. Open up “#{Rails.root}/app/assets/javascripts/routers/users.js.coffee” and add:
class OurAgendaApp.Routers.Users extends Backbone.Router
routes:
"": "index"
index: ->
OurAgendaApp.usersCollection.fetch(
success: ->
OurAgendaApp.usersIndexView.render()
)
Router files are neat in that they can take specific action based on the URL. In this example, we'll just setup our basic index route.
Now…make sure your “rails s” is shut down and reloaded in your terminal and go ahead to localhost:3000/ in your browser..and….badabing…you should see your eighth “Hello World.” Right click on it and “inspect element” and check out the tag that Backbone added with a single paragraph tag containing your string.
And yes, you guessed it, you can go back into your Rails “rails c” console in your terminal and create additional users and their first_name + last_name combo will show up at the bottom of your list when you refresh your browser. Pretty sweet. We'll get much more into Backbone.js details later, but now you should be able to at least conceptually understand how Backbone views, models, routes, and collections can be used to do all sorts of neat things.
- — -
Alright, we gained some serious ground in this Hello World trilogy. Congratulations — you now have the basic tools at your disposal to build modern, feature-rich web apps. In our next post in this series we'll jump back over to the branding and design side of things and get ready to merge in the pretty stuff to OurAgendaApp. Previous post: Step-by-step “Hello World” examples in Ruby, SQL, and Ruby on Rails.