JavaScript For The Impatient: BackboneJS (part 2)

Introduction to Backbone models and collections, and allowing the user to create blog posts in the ‘blog’ style web app.

GitHub repo


SCOPE

You should be familiar with variable scope. For example, a variable created inside a function is local to that function. Variables not declared within a function are global variables. Let’s go over an example:

var whatever = "global";  // declared outside a function-global
var functionOne = function() {
var whatever = "local"; // declared within functionOne - local
};
console.log(whatever);   // result -> 'global'

The example above shows that even though we have two variables with the same name, only the global variable is recalled when we log to the console. But, what if we logged to the console inside functionOne() ?

var whatever = "global";  // declared outside a function-global
var functionOne = function() {
var whatever = "local"; // declared within functionOne - local
  console.log(whatever + " I'm inside functionOne!");
};
functionOne();   // result -> 'local I'm inside functionOne!'

When interacting with variables inside functions, the local variable will always take precedence.

If you need more guidance on variable scope, read the Functions chapter from Eloquent JavaScript.

The point of this portion is to explain to you what all the ‘this.whatever’ business is. Let’s look at the DashboardView, for example:

var DashboardView = Backbone.View.extend({
template: Handlebars.compile( $("#dashboard-view-template").html() ),

initialize: function () {
this.render(); // <-- WHAT DOES 'THIS' MEAN!?!?!?
},
  render: function () {
this.$el.html(this.template({greeting:"This will display a list of our blog posts"})); // <-- AND IT'S HERE TOO! :(
}
});

Remember from Part 1, each view in Backbone has a render function. When we initialize the view (and we know that the initialize function of any view always executes — no other function will do this without explicitly calling it) we immediately call this.render();

‘this’ means this view. So, what is a view? In part 1 I told you that a view is an object, that’s all it is. You can refer to the properties within the view by using this. Initialize is a property, render is a property — their values just happen to be functions.

Because you’re working inside a function, you are working within the scope of that function.

So, inside initialize(), you can’t simply call render() — you would get an error, initialize() has no render(), and the view’s render() is outside the scope of initialize(). Instead, you say this.render() because the view has a render(). You are essentially saying “I need this view’s render function to run here.”

Got it? Cool. Don’t let ‘this.yourMom’ get you all worked up. It gets easier with time (like your mom).

Actually, one last thing about this:

render: function () {
this.$el.html(this.template({greeting:"This will display a list of our blog posts"})); // <-- let's talk about this 'this'
}

In Part 1, I explained that every view comes with a DOM element. In the above render(), I’m referring to that DOM element by saying this.$el.html. It’s essentially saying “This view’s DOM element’s html should be this view’s template.”

Ugh, I feel like I explained this whole thing like garbage. Leave me a comment with questions about this and I’ll do my best to answer as thoroughly as possible.

BASICALLY, ‘THIS’ JUST MEANS ‘THIS VIEW’…kind of… there are times where it doesn’t mean that… I’ll explain later.

MODELS & COLLECTIONS

It took me an absurdly long time to wrap my head around what exactly models and collections do, and how they work. This is because I’m extremely impatient and I never took the time to figure it out. I’m going to explain this to you in the most simple terms:

var Blog = Backbone.Model.extend({
idAttribute: ‘blogId’
});

This is our Blog model. There isn’t much to it, and that’s on purpose. Think of a model as a cookie cutter. A cookie cutter isn’t a cookie, but every cookie you make from it will be the same shape. Every cookie is an instance of the cookie cutter. Our model, Blog, is the cookie cutter, and it’s saying ‘every instance of me can be tracked by it’s blogId’. It doesn’t give, or require, any other information. (idAttribute is given to us by Backbone, check it out)

var Blogs = Backbone.Collection.extend({
model: Blog
});

This is our collection, Blogs. Again, there isn’t a lot to it. A collection is just a collection of the instances of the given model. Say we make 10 Blog cookies, and we put them in a box called Blogs. The box knows which cookie is where based on that cookie’s blogId. Likewise, you as the person with a hankerin for Blog cookies, knows which box to go to (Blogs), and which specific cookie you want.

You would never eat a cookie cutter, and you would never eat a box. But you would eat a cookie (the instance of the model) and you would eat cookies (a collection of those model instances). In that same vein, you won’t be creating multiple Blog models or Blogs collections, you will simply create Blog model instances as needed, and place them in the collection.

Let’s get some practical usage out of this, here is our new app.js:

First, we created a model: Blog. Next, we created a collection: Blogs. Then, on line 9, we did something…different. We created some generic blogs. I cut a couple cookies, if you will, and put them in their box.

On line 9, I instantiated the collection Blogs, and called it blogs. blogs is a collection of two blog posts, complete with their own blogId, blogTitle, and blogBody.

Continuing down, on line 23, I assigned DashboardView’s this.collection to blogs. On line 29, I passed this.collection into our template. (this.collection.toJSON() just allows us to work the data in the template — this is the reason for the JSON2 library we put in libs.)

If you’ve been paying attention, you should have guessed that from here we need to update our template to utilize theBlogs. Here is our new index.html:

The changes start on line 28, I’ve commented to help explain whats happening. Read the comments.

Line 39 introduces you to one of Handlebars’ built-in helpers, #each. We passed theBlogs into our template, we know that we have two blog posts in this collection, and we know we want to display them on our dashboard — so #each makes the most sense here. Everything between {{#each}} and {{/each}} will repeat until it has reached the end of theBlogs, in this case it runs twice (theBlogs is a collection of just two models after all).

Go ahead and open index.html in Chrome:

Looks nice! Good job!

CREATING BLOG POSTS

We understand what a model and collection is, and we understand how to work with them using Handlebars to display the values of their properties. Let’s update index.html to allow us to type new blog posts!

Our ‘Create A Blog’ template starts on line 55. I’ve commented it, but you should be able to tell whats going on without the comments. We’ve added an input field for the title (#blog-title), a textarea for for the body (#blog-body) and a submit button (.submit-button).

The reason I’m listing the id’s of these elements is because we’re about to use jQuery to pull the value of these elements. Let me show you what I mean, app.js:

The changes begin on line 35. Actually, technically line 38 — you’ll see I assigned CreateBlogView’s collection to blogs. This way, both the DashboardView and CreateBlogView are sharing the exact same collection.

Take a second to read all the comments I’ve added to this view, you should be able to make sense of what’s happening just reading from top down.

I’ve created a new Events: { }, this is another one of those fun things that each Backbone view comes with, we just didn’t have a need for it until now. We have one event:

'click .submit-button' : 'createNewBlog'

“When a button, only within this view’s DOM element, with a class of ‘submit-button’ is clicked, lets run this view’s createNewBlog().”

I could have done:

'click #submit-button' : 'createNewBlog'

I really just wanted to show that you can use a class and/or an id.

createNewBlog() looks really big, it’s not. The bulk of it is just assigning variables to values that we need to put in the new Blog model: newBlog. For example, we use simple jQuery to find what the user entered for a title:

var newBlogTitle = $('#blog-title', this.$el).val();

“Find the element, within this view’s DOM element, with the id ‘blog-title’. Awesome! Now, lets grab the value the user typed in, and we’ll store this in a variable called newBlogTitle.”

Pretty easy stuff once you figure out what it’s doing!

Naturally, on line 49, we instantiate the new Blog. We give it three properties, and we throw the variables in for their values. Finally, we have to add this newBlog to the collection, and we do this on line 59. I logged a couple things to the console just to show you that the model gets created (and so you can see the blogId which doesn’t display on the dashboard), and the collection grows by 1.

Go ahead and open index.html in Chrome. Create a new blog post, and view it on your dashboard. Remember that because we’re just running JavaScript in the browser, any time you refresh your blogs will be as if they never existed.

Good job! Doesn’t it feel good to create something!?

We’ll get into the edit and delete options in part 3.

Thank you for reading!

Show your support

Clapping shows how much you appreciated Corey Howell’s story.