What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Backbone.js Fundamentals
by Liam McLennan
Backbone.js is a set of tools that gives structure to client-side web applications. It helps us write clean, maintainable JavaScript applications.
Resume CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Learning Check
Recommended
Introducing Backbone.js
Overview
Hi. I'm Liam McLennan. This module is the first of the Backbone.js Fundamentals course. The course will introduce Backbone.js and show how it can be used to build advanced client-side user interfaces with HTML and JavaScript. This module, Introducing Backbone.js, provides a high level view of the library and its various components. Step one and very important for this course is the discussion of the course prerequisites. Then I'll quickly introduce Backbone and explain its purpose. Backbone supports a certain style of programming. So I'll explain what a typical Backbone application looks like as well as the pros and cons of the architecture that Backbone encourages. The Backbone is a small and simple library. It doesn't do anything that you couldn't just code yourself. So why do we need it? Finally, we'll work through a basic Backbone application. The demo, we will we use many concepts and features that we weren't have covered yet. So if you are new to Backbone, you just have to trust me a little bit. I've included an example just to show an early taste of what Backbone can do and what a Backbone application looks like. Pre-requisites. Before beginning this course, you will need some foundational knowledge. To be able to focus on Backbone and its usage, I've had to assume some basic programming skills. In particular, you will need to know the basics of JavaScript. That doesn't sound like much, but it's amazing how many web developers have used JavaScript for years and still don't understand how it works. If you think that your JavaScript knowledge could do with a bit of a refresher, put this course on pause and go complete Pluralsight's JavaScriptFundamentals course. It will provide all the JavaScript knowledge that you will need to successfully understand this course and will be enormously helpful with your Backbone.js application programming. Other required skills are HTML and CSS and general web programming. As you progress through this course, I encourage you to work through the examples yourself, research for the details if you have a question that I don't cover and experiment as we go. In the course exercise files for this module, I have provided an empty HTML page with all of the Backbone.js dependencies included which you can use for your experimentation. I'll demonstrate how to do that later in this module.
What Is Backbone.js?
Backbone is a JavaScript library that provides helpful types for building and organizing rich JavaScript interfaces. It's not a framework. The common distinction between frameworks and libraries is frameworks call your code and you call libraries. Another way to think of that is that frameworks control how you design you app. They specify things like where you put what files, what you can do, and how you do it. That's great for getting started and it's great for getting things done quickly, but it's terrible for flexibility. Libraries provide some useful features and then you're on your own. Backbone, for example, provides help with the number of common user interface features, but it doesn't help at all with how you design your application. That's both a strength and a weakness. This course will provide some guidance for how to build Backbone.js applications. Backbone is sometimes described as an MVC framework, but it isn't. In fact, Backbone doesn't fit neatly into any of the well-known user interface patterns. It's not MVC, it's not MVVM, it's none any of them. It's a whole new pattern designed specifically for the web and interactive client-side applications. Backbone is a library of tools that help build a richer web. The definition from the Backbone documentation is, Backbone.js give structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface. I think that's a reasonable definition and it begins to introduce some of the components that make up Backbone.js. That definition came from the Backbone documentation which is a single page posted on GitHub. You can see the link here. So this page includes documentation of all the Backbone types which you can navigate easily using the table of contents on the left. It has links to download Backbone.js. You can choose from the fully documented source or the packed and gzipped Production Version. There's also links to example, Backbone.js applications and F.A.Q. and the Backbone Change Log. Now, let's think about the architecture of a Backbone application. Backbone is a JavaScript designed to run on the browser. So, obviously, building apps with Backbone involves moving site away from the server and into the client which is the browser. When a user navigates to you Backbone application, the application source is downloaded to the browser. New client-side application consist of Routers, the handling page transitions, Views for rendering Models, Models for representing the dotter (phonetic) in your application, and Collections for managing many models. The server's responsibility is to serve the initial application and then to provide the RESTful web services for the client-side models. The server and the client exchange serialized models as JSON data over the HTDP particle. What do you think of some of the pros and cons of this architecture? Firstly, it's fast. There is an initial cost to download the application upfront, but this can be cached for subsequent visits. After that, the network traffic is just the JSON models and even that happens asynchronously. So the user hardly ever has to wait for the network. From the user's perspective, the application's usability is greatly improved compared to traditional web applications because they receive immediate feedback in response to their actions. The application is running on the client, so there's no round trip involved to process every single interaction so the perception of speed is much faster. The application feels much closer to a desktop application experience. As your application becomes more popular, these architecture scales well because much of the work is transferred to the client. The serves only has to revive the rest API which can be easily optimized. As software developers, we know that most things are tradeoffs. The substantial benefits of client-side applications are balanced by challenges. Client-side applications are not automatically indexible by search engines. When Google scroll or visits your application, what it sees is an empty page. Backbone subsequently builds your user interface inside that empty page, but the process is invisible to the search index. There are ways around this, but if it is required. Client-side applications are relatively difficult to test. It's easy to write code that depends on a functioning document object model or DOM which means that code cannot be tested outside of the browser. It's also easy to introduce complex relationships between components to communicate indirectly borrow events. To test such components requires carefully breaking them apart. And finally, client-side code has some unfortunate security implications. Uses of you applications have the source code and they're free to debug your program and modified as they please. This must be taken into consideration and security must be enforced by the server.
Single Page Applications
SPAs or Single Page Applications are becoming the standard for high quality web applications. A single page application is a web application that loads all of its resources as a single page load and then executes in a single web page. Client-side scripting in Ajax are used to provide user interaction instead of regenerating pages set aside. This technique is used to provide an improved user experience until they bring faster and more advanced user interaction. SPAs are the results of an evolutionary process. In the beginning, the web was a connected set of static pages. After that came server-side applications. Each user action resulted in an independent request to the server-side application which would respond with a completely regenerated page. In this model, the user is constantly interrupted by network latency and page redraws. Actual performance suffers because the redundant work being done to constantly regenerate the page and retransmitted over the network. Because entirely server-side applications unlimited and boring, web developers begin to enhance them using client-side scripting and Ajax. This is the hybrid model of web development. And finally, SPAs develop us by completely moving the use interphase, logic and generations to the client. The responsibility of the server is reduced to the initial serving of the SPA, providing data services and any other activities that cannot be performed on the client. User interactions result in immediate feedback just like a desktop application. And much of the server communication can be done asynchronously without the user having to wait and being interrupted in their task. When a person uses your application, they are trying to perform a task or achieve a goal, repeatedly forcing right time into the process as server-side web applications do breaks the users flow and delivers a substandard user experience. The hybrid model is popular at the moment because it promises familiarity as well as an improved user experience. However, it is a fraud and impure architecture. When developing a hybrid application, it is never clear where responsibilities belong. It can feel messy and random. Gmail is possibly the most famous single page application. Gmail users can search, scan, read, send and delete their email without a single page reload. Facebook, like most SPAs, is not strictly a single page. SPA is a concept, not a strict definition. It often makes sense if your application to consist of a set of SPAs. Twitter.com is an SPA that makes use of model dialogs to squeeze more information into the user interphase. Trello is a fancy to do this application. Let's have a quick play with Trello so you can see what an SPA is like. This is Trello. Trello is a web app used to manage list of anything. Here I have three lists, basics, intermediate, and advanced. Each list has a number of cards. I can open a card by clicking on it to see more detail. I can change a text field by selecting it and editing it in place. I can add new cards. I can reorder cards within a list, and I can move card form list to another. For any given card, I can add a checklist and then add things to the checklist. ( Pause ) And check those off as I'm done. I can give a card some label, choose a color for the cards label, give the label (inaudible), I can add an entirely new list to this board and reorder list. If I'm working in team situation as somebody else is using the same trial board at the same time. If my team member adds a card-- ( Pause ) It's automatically added to my Trello board as well. So this some same real time capabilities, they are all sort of soft real time. And if they were to archive that card, watch it disappear. So the two boards in different browser Windows is dancing. Trello is an excellent example of what can be done with client-side web applications and the single page applications if you prefer that term. It's also a great example of a Backbone application. And I think a very good case study of why the style of application can deliver a advanced either experience. It's not all good news. SPA is introduced some unique challenges. SPA is not new but it fair to say that the technology is immature compared to the server-side web frameworks is a lack of tooling and experience developers. When Google is part as an SPA, it receives an empty page because the content has not been generated yet. If search engine is important for your application, then you'll have to do some extra work to make your content visible to search engines. This will be covered later when discussion Routing. Running is a substantial portion or your application client-side increases your dependence on the user's browser. And some browsers are a lot better than some others or becoming this means that you may choose not to support some older browsers. Also, you want to follow standards for HTML, CSS and JavaScirpt and you'll need to thoroughly test in multiple browsers.
Why Do We Need Backbone.js?
Creating client-side applications without using a framework or library is hard. Often, it degenerates into a tangled mess of nested go backs and event hand list. Hr need help to develop coherent, readable, structured, testable, and maintainable client-side applications. Backbone contains tools and codified knowledge that minimize the difficulties. Backbone provides a set of tools for introducing structure into client-side applications. Models represent the data required by our application. They hold your applications data and raise events when data changes. Collections, group models, they also for the events from the models they contain as well as events that they earn. The arrow here represents the dependencies. So, collections depend on models. Backbone connects the various components indirectly using events. Events travel in the opposite direction to the dependency shown. Views handle event from models and collections. They are responsible for rendering any mark up. Views are the only backbone component that typically interacts with the document object model. So views can also handle events from the DOM. Backbone routers are used to simulate page changes and to provide support for page history and bookmarking. What I've shown here are not rules, but more like a starting point. In particular, a router may optionally depend on models or collections. What's shown in the diagram is the simplest to configuration. Backbone is a lose collection of helpful components. You can use them anyway that make sense to you. Backbone has two required dependencies. Underscore.js is a library that provides functional programing support for JavaScirpt. Backbone provides many functions from Underscore.js as methods on Backbone collections. Even when not using Backbone, Underscore is a tremendously useful library. For dealing with the DOM and for making Ajax request, Backbone requires jQuery or zepto. Zepto is a lightweight alterative to jQuery with a largely compatible API. Zepto is popular for mobile projects, but usually don't have to support very many browsers.
A Minimal Backbone.js Environment
This is the minimal environment that I will use and I encourage you to use for testing Backbone. I can open this page and use the browser consult to experiment with Backbone. It imports jQuery, Underscore, and the Backbone scripts. Otherwise, the page is empty. So this is the same HTML file and I opened it in a browser and appears as an empty page. I'm using Google Chrome so I can press F12 and open the Google developer tool and select the Console tab to work with JavaScript. If I first have a look at the Scripts tab, I can have a look at the scripts that are currently loaded and as I'd expect, you can see that jQuery is currently loaded, Underscore, and Backbone. To begin with, we can have a look at the Backbone object. So the Backbone object is in scope within this page. So it's important so that we can work with some Backbone objects and you can see all the Backbone objects that we've talked about so far are visible here. Models, Collections, Routers, Events, View, and some others that we haven't spoke about yet. We can have a look at Backbone.Model, so this is the Backbone type that you use to represent the data in your application. A Backbone.Model is a function and this function is a JavaScript to construct for creating new model objects. For example, if I wanted to create an object that represents a book, I can use the new keyword, ask for a new Backbone.Model and I can provide some properties for my new object. So I going to have one property called title with the value "White Tiger." I'll have an author property with the value "Aravind Adiga." So the expression has returned undefined which is okay because my new Backbone.Model object has been created and has been assigned to the variable book. So if we look at book, you see it's a Backbone.Model with various properties that belong to Backbone models. If I wanted to retrieve a property from my model, I use the get method so I can ask for the title property, get title, and it returns to White Tiger which is the title that I set in the contractor. Backbone models don't store the data directly as properties on the object which is why you have to use the get method if you want to read the property and you have to use the set method if you want to write a property. So let's try writing a property. I'll update the title to "The Stripey Tiger." That's done. If I inspect my model to see what it would like as JSON object, you notice that the title has updated to the Stipey Tiger and the author remains unchanged. This empty Backbone environment is useful for doing this kind of ad-hoc Backbone testing. If you just want to see how Backbone behaves with certain code and certain experiments. jsFiddle is an online environment for experimenting with HTML, JavaScript, and CSS, so all the technologies of the web. I've created a preconfigured jsFiddle that you can use for working with Backbone.js at the URL seen here. So what I mean when I say that it's preconfigured for working with Backbone is that, it is already included jQuery 1.7.2 and if I have a look at here in Managed Resources, I've also pre-included Underscore.js and Backbone.js. The format of jsFiddle is this screen is divided into four working areas. The top left area is for defining some HTML Snippet. The top right is for CSS styles to be applied and the bottom left is for JavaScript. And when you click the run button, jsFiddle will apply the HTML, CSS, and JavaScript that you specified and display the output in the bottom right rectangle.
A Backbone Example
We've come a long way, but so far it's been mostly theoretical. Before we continue, I'd like to end this module with the practical demonstration of what it's like to build a simple application with Backbone. The application I'm going to build will be capable of rendering rectangles on a canvas. Each rectangle will be represented as a Backbone model object with properties for width and height, position, and color. A Backbone view will have responsibility for rendering a model. The starting point for our application is a slightly modified version of the empty Backbone environment that we saw earlier. Again, it includes jQuery, Underscore, and Backbone, but this time, there is some content in the document body, a heading and an empty div with an id of canvas and also I've included another script called rectangles.js. This is what rectangles.js currently looks like. It's fairly empty. As a matter of style, I've included an empty function and the function is wrapped in parenthesis and then that function is immediately invoked. This is what's known as an immediate invoked function and it's a useful tool in JavaScript for grouping code together and preventing variables from leaking into the global scope. So I'll be defining my application inside of this function wrapper. A natural starting point is often to begin by defining our models and from my application, I want to model to represent a rectangle, but there's nothing special about this model. So I can use a clone of the built-in Backbone dot model type and I'm just-- I'm going to name it Rectangle because of what we're representing. Rectangle is my application, so I can just say that rectangle is equal to Backbone.Model.extend, and I'll extend an empty JavaScript object. So this gives me a new type, rectangle, which has no special capabilities beyond what's provided by Backbone.Model. Now that I have type for my models, I need a type for my view. I'm going to call it RectangleView and I can create a view by extending Backbone.View. To get this application to do anything useful, I am going to use a lot of Backbone capabilities that you haven't seen yet. Don't worry if you don't understand every detail as I go through. Everything will be covered in later modules. I just wanted to get in early and demonstrate something useful and provide a field for what it's like to work with Backbone. So I've defined my view type, RectangleView. No need to add some properties to it to customize it for my purpose. The first one is tagName. So this is the type of element that I want to be rendered for this view. Similarly, we have className, so this is the CSS class that will be applied to the DOM element that represents the view. I use a class of rectangle. Not every Backbone view can have a render function but this is the function that will be called when we want to take our view and render it into the document. So in the case of my RectangleView, this view is responsible for providing a visual representation of my rectangle model. The first thing that my render function is going to need to do is to set the dimensions of its rectangle. So I can define any arbitrary functions on my view. So I'm going need a function called setDimensions. Within the setDimensions function, I want to access the DOM element that's bound to this view and I want to set its width and height. And in Backbone, I can access the DOM element or in fact a jQuery was up to a wrapper around the DOM element using the dollar sign el property of the view object. Because this is a jQuery wrapper around the DOM object, I can use the jQuery CSS function for the setting of width and height, and the syntax for that is to rewrite an object with the width property. I'm going to set the width to whatever the width property is on the view's model. So to get a property for a model, remember we use the get method on the model. So I'm going to get the models width and just depend px to it for pixels and do the same thing with height. Access the model, called the get function, ask for the height property, and depend px to the end. That's it for the setDimensions function. When that's called, it's going to apply the model's dimensions to the DOM element. Of course, that's not enough to render my model since the rectangles also have a position. So I can define another function called setPosition. This is another custom function on my view, setPosition. This function with very similar to the setDimensions function that I previously defined. Again, it's going to access the DOM element for this view and position it according to data around the model. So these are variable called position which will be set to the position property from the model. ( Pause ) Again, access the jQuery wrapper for the DOM element of this view. Use the CSS function. This time, I'm not setting within height. I'm going to be setting the left CSS property which I'll set to position.x. So the X coordinates of the position property of my model and I'll set the type CSS value to the position.y. So that's the Y coordinate of the position of my model. So now we've done enough to set the dimensions and position of some rectangles. I'll make one final change to my render function and that is to return the view object from that function. That's just a convention that people use when they work with Backbone. So at this point we have model and view which is enough to get-- to do something. But to kick off the actual process, we need to actually create objects that are instances of our model type and our view type. So, let's start with a rectangle model which I call myRectangle. And to create an instance of my Rectangle type, I use the new operator. So I want a new Rectangle and now I have to set the property values of that object. So for this one I might say that it has a width of 100, a height of 60, and position, because position has an X value and a Y value, positions defined as an object and give it an X property of 300 and a Y property of 150. So that's enough to define my first rectangle model. Now that I have a model, the last thing left to do is to create an instance of my view type. I can call this myView new RectangleView. When I create my new view object, it's important that I give it a reference to the model object. So I have to tell the view which model it is that it's responsible for rendering. So this view object can be responsible for rendering myRectangle and that's enough to create an instance of the view object. Because I want to actually see the application, do something, the next step is to render the view and add the resulting element to the HTML page. I can do that by using a jQuery selector and I'll select the div with the with ID canvas which is an empty div that is defined in my HTML page. Here, to that div, I want to append, it's just a standard jQuery function, and the value that I want to append or the content that I want to append is myView.render, I need to render that view and once it's rendered the content is in the dot el property. So that is the DOM element that's created by myView. So that's the starting point. We have code that it defines a model type, a view type, instantiate instances of that model and that view and then renders the view and writes its content to the page. If I open that in Chrome or the browser of your choice, if I refresh the page here, I see my rectangle. So that's the rectangle that I asked for, 100 pixels wide, 60 pixels high, and positioned to X value 300 and Y value 150. So that there is a probably about the most basic completed Backbone application I could possibly have. To make it a little bit more interesting, let's add some color. Back to my rectangle view and the render function, and now I want to say, let's set the color of my rectangle as well. Setting color is very similar to setting dimensions and setting position. To find another function on the myView, again, access the jQuery wrapper of the view element. Use jQueries CSS function, and I'm going to set the background color. The value for that will come from my model so I need to use the get method and get the color value, and that should do it for set color. Since we're now using a color property of the model, I have to add that to my model object. So I simply add another property called color and give it a color value. Have a look at the application running again. You can see that my rectangle is now red. I talk a lot in the introduction about how Backbone is good for interactivity and yet of created application here with no interactivity whatsoever. Let's make it respond to user input. What I'm going to do is I'll make it so that when the user clicks on a rectangle, the rectangle moves. Backbone views are capable of responding to events, either events of backbone models or events of the DOM. And why we do that is we setup declarative bindings of events to functions. So for my rectangle's view, I'm going to bind the click event to a function called move. So the move function does't exists yet. So I'll add it to the list of functions on myView, move. And what I'd like this function to do is to cause my rectangle to move 10 pixels to the right. So I need to access the element, again, I'll use the CSS method. I'm going to set the CSS left value and I'm going to set it to 10 pixels more than it was. So I read the current value of the left property and then add 10 and apply that. Have look at our application again. Now, when I click on the rectangle, every time I click it moves 10 pixels to the right. Because my model and views are types, I can instantiate as many instances of them as I'd like. Now, what I want to do is put a few more rectangles on the page by creating a few more rectangle objects. What I do is I'll change my rectangle and change that to an object called models and instead of being a rectangle I'm going to make that an array containing rectangles. Now, my model is in array. I can add a few more rectangles and then I'm going to change their properties so they're not all the same size and the same position with the same color. My code that renders the view still is just going to render one model and add it to the page. So I need to change that. I can use undersore.js to access my models array and it has an each function. So it's going to call the function that I'm defining now once for H model in the array. So this is just really a way of having a four loop over the array. So I'm going to take a copy of my existing code that adds the model to the page and put it inside the loop. But instead of referring to myView, I'm going to modify that to create a new RectangleView which will be for the current model of the loop. And other than that it's the same. We create a new view, call it render method and then append the element to the page. Delete the other one, just like that. Now, instead of having one view and one model, I've got an array with three models-- loop through the array and for each model creates a view, render it, and add it to the page. Again, we go back Chrome, reload the application and we see-- we now have three rectangle models and each one, if I click on that-- the model that I click on moves 10 pixels to the right for each click. So have you enjoyed that? That's an example of creating a very simple Backbone.js application that has models, views to render the models, and some basic handling of DOM events. ( Pause )
Summary
Web applications are becoming more interactive. We want to make them faster and more like this to web applications. But developing web applications with client-side state is hard, and the tools are relatively immature. We need some help. Backbone is a library that helps organize client-side applications. It's not framework and it's not MVC.
Models
Overview
The previous module of this course introduced Backbone JS, explained why it is helpful for the modern web application developer and provided a quick demonstration of some of its features. Now, it's time to begin examining Backbone in detail and the best place to begin is with models. Backbone is a collection of loosely components so it is certainly possible to build an application with Backbone and not use Backbone models, but you probably won't and I'll explain why. I'll demonstrate how to define your own model types, how to instantiate new model objects, how to get and set model properties, and how to use model events. Model identity has complexity because models are often synced to a server. The model has identity in your client application and the model has identity on the server. Backbone provides some support to help maintain the identity of model objects throughout their life cycle. Backbone models can declare default property values and finally, we'll look at Backbone support for model validation.
The Purpose of Models
Models are the foundation of your user interface. Models can contain their applications state, as well as logic and behavior. Most of the concepts involved in your application will be represented as models and those models will contain your application's data. Why do we need a special type for creating models? JavaScript has object literal notation built in that already provides an efficient way of declaring new objects. Well, models provide a life cycle. Objects are created. They go through a series of changes. They are validated and synchronize to some date source. And, models communicate state changes to your application by raising events. Through this mechanism, changes to a single Backbone model can ripple through the user interface without any direct coupling or redundancy.
Defining New Model Types
We can create new model objects directly from Backbone.Model but most of the time you'll want to define your own model types. This is done by extending Backbone.Model. The argument to the extend method is in objects containing the configuration of your new model type. Passing an empty object results in a new model type identical to Backbone.Model. In this example, vehicle is a new constructive function or pseudo type that inherits from Backbone.Model. We identify our Vehicle begins with an upper case V because that is the conventional way of naming constructive functions in JavaScript. The extend function is a helper shared by model collection, router and view. It establishes an inheritance relationship between two objects. A similar function is also found in underscore dot js and in j query. Let's have a look at how this might work in practice. The web page you can see here is the jsFiddle that was introduced in the first module of this course. It's an empty jsFiddle except that it includes j query underscore dot js and Backbone dot js. So I can begin immediately working with Backbone types in the JavaScript editor here. And what I'd like to do is to find a vehicle type, we do that by extending Backbone.Model. And I'm just going to add one simple property to my new model type. It's the property called prop1 with the value a string containing the number one. And now I can instantiate new models from my new type just by using the new operator and the vehicle constructive function. So, a current object called V, another object called V2. So now, I have two instances of my vehicle type. I can access the property on the first object and so its value to something different. And now, if we use console to inspect the value of the properties of my two objects, firstly the first object and then the second object. If I run that, and you look in the JavaScript console, you can see that the first object, property one is equal to the value one which is what I set and the second objects, property one is equal to the numerical character one because that's how it's set when the object was created. ( Pause ) It is possible to define class properties by providing a second argument to extend. Class properties are like static fields in C sharp or Java or like class methods and class variables in Ruby. They become available directly on the type instead of on objects that are instances of that type. In this example, the summary function is available directly on the vehicle constructor without the need to first create a new object from the vehicle constructor. Let's try that in jsFiddle. We create a new constructor for a model called vehicle. As usual, by extending Backbone.Model. The first argument to extend is where we would usually configure our model object. In this case, I'm just going to pass an empty object. Now the second argument is where we can define class properties. So with my example was to add a summary function which is going to return some texts. ( Pause ) Now that I've defined a new model type vehicle, I can call my summary function even without instantiating a new object of the vehicle type. And if I send to that to the console, now we can run this example and see what it does, click the run button and it outputs, vehicles off for traveling.
Instantiating Models
Backbone model types are JavaScript constructive functions. Therefore, to create a model object, call its constructive function with a new operator. If there's nothing unique about your model type, there's no need to define a custom model type, so you can just use the Backbone.Model constructor. Usually, you will be instantiating custom types. Vehicle is a custom Backbone model constructive function and ford, is a new object created from it. It's common to provide a models' data as an argument to the constructor. In this case, I'm setting the name and the age properties for my newly created model object. Initialize is a special model method that if specified, will be called when the object is constructed. So it's a lot like your typical constructor that you would have in a class in C sharp or Java or many other languages. This is our empty model definition that we've seen many times, so we can just specify and initialize function and I'll just get that to write a message to the book, vehicle created. And now, if I instantiate a new instantiate a new instance of my vehicle type using the new operator, run the example. When he constructor function was called, the initialized function was invoked and its output the message vehicle created.
Inheritance
Models can inherit from other models, just as we extend the Backbone.model to define our own model types. We can further specialize by extending any model type. In this example, vehicle is a model type that extends Backbone.Model. Car is a model type that extends vehicle. I'm going to define a constructive function A that extends Backbone.Model. I'm going to give it an initialized function as we saw previously, initialize is like a constructor, it gets cold when we create a new object of this type-- I'm just going to use it to log the new objects creation, so it's going to say initialize object to type A. And once again, I define another method on my A top which will be called as string, so this is sort of like two strings function, it's going to provide a string or presentation of the state of the object. You can use JSON.stringify, so that's built into all old or modern browsers and if you need to spot on all the browsers. It's available in the JSON2 Library. And give it an object to serialize. So I can do it by calling this, so this is a reference to my model type and the two JSON method. So that whole thing is going to give me a string representation of my model. So let's check that that works. Create a new object A, which is going to be a type of case A, provide some values. And now, I can log A as string. So I'm calling the string function or method on my new object A and that's going to output a serialize representation of As current state, that run-- okay so, initialize A and that's the result of the initialized function and this object here is the JSON serialize representation of As data which is the properties one and two with the associated values, that's all fairly standards stuff. Now, to demonstrate inheritance, I'm going to create a new type B and B will extend to A. And now, I'll create an object B which is of type B, give it some data as well different to before. And again, log the state of B. Now this is the interesting right here because I'm going to be calling the asString method on my object B, but my definition for the type B didn't define an asString method. So the ability to call asString here is by virtue of the fact that B is inheriting from A and A defines asString. So if you look at what that does, so initializing two objects now, the first is I want to create my object, the second is when I create my B object. Let's us say it's initialized A because that's the message that's created in As initialized function, so I didn't overwrite that for B. Again, it's just outputting the serialized state of the object B by calling the method on B super type. So there's a few more interesting things that we can have a look at. If we use JavaScripts instance of operator, we can test the type of objects. So if I say the instance of my type B, you see the output is true, it's as what we expect B is an object or an instance of the type B. What about A if we say B instance of A and run that because it's true again, so it would be and it's correctly telling me that B is an instance of A, so it's a derived type of A. And if we take it one little further, we can ask B if it's an instance of Backbone.Model because B inherits from A which inherits from Backbone.Model. On that again, the output is true and just to prove that is works. What if I was to ask A if it was an instance of type B? So remember, B inherits from A not the other way around. So A is not an instance of type B, so expect false.
Attributes
Model attributes hold your data. They can be set by providing an object to the model type's constructor which we looked at earlier when talking about instantiating objects or by using the set method. The set method is passing attribute name and value to set or to set many properties at once, set can be passed in object containing many name value of this. Attributes are accessed by using the get method. Escape is like get, except that the output is html escaped. This is used for preventing cross sites scripting attacks. To demonstrate getting set I need to use my empty Backbone page, its got Backbone.js underscore js and jquery already learned. The first thing I want to do is define my familiar equal class and I would give it a function called dump, and dump is going to serialize the state of the object and write it to the console. Just to make it easier to inspect the value of my objects. Okay, so the vehicle class is now defined. I can create an object of the vehicle type, get some data. So, have a look at my new object, you can see that its got one property type with value car. So that was the first way that you can set attributes to an object by passing a object to the constructive function. But I can also add attributes using the set method. So I'm going to set an attribute color with the value of blue. When we inspect my object, see it's now got two attributes. If I want to add more than one attribute at once, I can use (inaudible) of the set method that takes an object-- going to give my object a description attribute which has a string of html with the value and a weight property (inaudible) value. Right, so inspecting the object again, we now have type attribute, color attribute description which is a string of html and weight which is a number. Now, I want to take the value of the description attribute and write it into the html document that I have here. So using j query, I can select the body element obviously apend method to add some content to the body and I'm going to access, use the get method to read my attribute from my object, I want the description attribute. So that's going to write my description attribute into the page, let's see what happens. Because my description attribute was a string of html containing some JavaScripts, when I was injected into the page, the page evaluated the JavaScript and cause this a lot of window to open. And that's a fairly big security problem, you often don't want people to be able to execute their own JavaScript in your page. So instead of using get, Backbone provides a very similar method called the escape. So escape also will read the value of attribute, but before returning it, it's got a html encoded which makes it safe to put into a page and work get evaluated. When I run that, you can see that it's simply added the description property into the page. Given a model object, you may wish to determine if that model contains a value for given attribute. Has is a predicate method that indicates if an attribute has been previously set. The code here is should hopefully be quite familiar by now. I am defining a new type vehicle which inherits from Backbone.Model and then instantiating a new object called ford which is of type vehicle and has a single attribute called type with the value car. If I want to test if my ford object has an attribute defined, for example test if it has a type attribute, I can use the has method. So I'm going to log if my ford object has an attribute called type, then we expect this to be true, so it is. And if I was to check for some other attribute that doesn't exist, for example year-- so my fourth object does not have a year attribute, so I expect has to return false, and so it does.
Events
Model events are one of the most valuable features of Backbone models. Events are the reason why the get and set methods are required when working with the model's attributes. By wrapping attributes in get unset, Backbone has the chance to detect changes and raise events. Models raise events when their state changes. To detect the change to a model, listen for the change event. Listening for events is accomplished by calling the own method and supplying the name of the event to buy into. The second argument to the own method is a function to execute when the event is triggered. The change event is triggered whenever the model is changed. It's also possible to listen to changes to a single property. This is accomplished by registering a handler for the event named change followed by a colon, then the property name. This technique of naming events with multiple parts separated by colons is called event name spacing and it's merely a convention. You can and you should, use it yourself when you need to organize your own custom events. The example shown here is registering an event handler for the ford object's change event to the color attribute. When the color is changed, this call back function will be invoked. ( Pause ) I began here by creating a new object called ford. It's a backbone model with two attributes, type and color. I would like to bind an event handlerr to the change event. So it's bonded to that handler, we use the on method on the object that raises the events. The second argument is to call that function to invoke when the event is triggered, and we've seen that call back and get it to log a message indicating that something is changed. To trigger that event, I use the set method, I'm going to set the type to truck. So we're calling set, calls this a changed my ford object which is going to trigger the change event and log my message if something changed. Let's run that, see I'll put something change as expected. If I'd like to be a bit more specific, I can register another event and the game for the change event, but this time, we're going to narrow it down to changes to the color attribute. And within this event handler, again I'll log a message that this time, I'll say that the color is changed. If I run the example now, I'm still just setting the type property, so setting the type triggers the something changed event which was the change event, but you notice that it hasn't triggered my color change even, so it didn't get the color change method. If I was to modify this to set to color property. Now, when I run the example, I get both events triggered. Firstly, the color change event because I changed the color attribute and also, the something change event because the model has changed. ( Pause ) It's possible to define, trigger, and observe custom model events. Events are identified by string identifiers. The identifier can be anything. Use whatever makes sense in your application domain. The convention is to name space events using a colon. Use the on method to bind to an event. The second argument on the on method is a call back that will be executed when the event is triggered. Use the trigger method to trigger an event. Subsequent arguments to trigger after the event name will be forwarded to the event hand list. There's also an off function that is used to remove event handlers. The event methods on-often trigger are provided by the events module and can be included in any JavaScript object, so it not just background models. Events are a powerful tool for decoupling components. Again I'll demonstrate how we can use custom model events and I'm going to demonstrate this time, not even using a background model, but just using a regular JavaScript object. So we declare an object called volcano, I'm going to use the underscore library and its extend method which is very similar to the extend method, that's included with Backbone. So I'm going to extend an empty object and mix in the Backbone.Events object. So what this does is it adds the content of Backbone to events to an empty object and returns that object which becomes my volcano object. So now, I have an object with event and really this is just a convention to make it easier to see what's going on. As far as the even t handling parties concerned it's all just one string that uniquely identifies the type of an event. As usual, I then provide a call back function, that's going to handle that event. I was in my call back . I got a lot of message, that say duck and cover. I can trigger my custom event using the trigger method and the first argument is the name of the event that we want to trigger, just disaster eruption. At this point, I can run this card and it ought to trigger the disaster eruption event which is going to log the message, duck and cover. Now, if I decided that I no longer wish to be able to handle disaster eruption events, I can use the off method to remove the event handler. So if I say, off disaster eruption, that's going to remove every event handler for that particular event. So after that, if I attempt to trigger the disaster eruption event, the event will be triggered but I've removed all the handlers so we would not see any output. So lee it's run but it's not output in the messages.
Identity
When you have many model objects, you need a way to tell one from another. Backbone gives each model object an id property. The id property represents the model's persistent identity. It is undefined until the model has been saved. When the model is saved, the id property is set to the server's identifier for the model object. The cid property is a temporary indentifier used until a model is assigned its id property. Once a model is saved and acquires an id property, the cid is no longer required. Model objects have an eased new method that can be used to test if the model has been saved to the server and has an id property. Here, I've created a brand new Backbone Model called ford. If we take a look, at its id property-- so this is the id property of the newly created model object. The id is undefined and that's because that ford object hasn't been persist to the server yet, so it hasn't been given its id. But if we take a look at its cid property, the cid is still zero. C id ZID is flood as soon as the model object is created. And finally, is we call the isNew method, so isNew will return a boolean indicating if this model object has been saved to the server yet. It tells us that the ford object is new, is new and is true meaning that it hasn't been saved to the server yet.
Defaults
When you define a new backbone model type, you have the opportunity to just specify default attributes. The default's property specifies default values to attributes that are not set in the constructor. Specifying defaults has the additional benefit of documenting a model type properties. In this example, I define default values for the color and type attributes of the vehicle type. When I create an instance of the vehicle type, the color and the type attributes are automatically set to the default values. This is the vehicle type definition from the slides. As usual, we extend the Backbone.Model, but this time, we're providing a default's property. And the default's property contains two properties, color with the value of white and type with the value of car. When I proceed to create a new vehicle object, I'm nit initializing any attributes in the constructor. If I then log the values of the color attribute and also the type attribute, we expect that they'll have the default values already set. And so the value's output, the white and car which are the default attribute values.
Validation
Backbone exposes model validity through two methods, validate and isValid. Validate test the validity of the model and returns any errors found. IsValid returns a boolean indicating if the model is currently valid according to the validate method. It's possible for a model to become invalid because set can be called in a way that does not trigger validation. Validate is called by Backbone prior to performing set or save operations. Set is the function we used earlier to set the value of model attributes. Save is a function that is used to persist the model. If the model is found to be invalid, the operation is canceled and an error event is triggered on the model. To add validation to a Backbone model we've run implementation of the validate method when we define a model type. The validate method has an argument containing the model's attributes. I'll create an object of the vehicle type. In order to detect the validation problems, I need to register an event handler for the error events. The car object will raise an error event whenever a validation problem is found. The call back that handles error events receives a reference to the model that raise the error and the error itself. So in this handler, I'm just going to log the error. At t this point, I can set some values on my car object. So when the foo attribute is set to the value bar, that's going to trigger my validate method which currently has no implementation, so there would not be any error raised at this point. We'll run it now just to check, and so it's fine, there's been no error raised. Okay, so the next step is to implement my validation. For this vehicle, I would like to validate the vehicle's color. I'll start by defining what is a valid color. So I'll creator an array of valid colors, white, red, blue, and yellow. If my vehicle's color attribute is one of these four values, then I'll consider that to be valid, otherwise the color attribute is invalid. So next, I'll define a function, it's going to check if my model object has a valid color, call it color is colorIsValid to function. Its arguments are the attributes. The first thing I'm going to check is if my model even has a color attribute. So what this will do is if my model doesn't have a color attribute, then I'm just going to return true. Know that color attribute defined is considered a valid value. Here. I'm using the underscore library and with underscore, I'm going to wrap my array of valid colors and use the include method to search for the model's color. So this is going to return true if the valid color's array includes the current color attribute of my model. So having defined my color as valid function, I can now use it. If my models color attribute is invalid, then I'm going to return an error message. Color must be one of in a list of what my valid colors are. So now, if I attempt to set an invalid color, and first I will attempt to set a valid color, so blue is a valid color. Set the color attribute to blue, run that example. I get no output because there's no error. However, if I now attempt to set the color to mauve which is not in my set of valid colors. I would expect that my color validation function is going to fail and it should return the error message. So that's triggered the model's error event calling through my event handler which has logged the error message, colors must be one of white, red, blue, or yellow. If I then were to check the current contents of the color attribute, log that to the console. The color attribute is currently undefined because setting the color to mauve failed, so that operation was canceled, the color attribute is still on set at this point. If I were to set it back to red, that operation will succeed. So then when we try to read the color, we'll get red. One slot variation is if we choose, when setting an attribute value or an array of attributes, we can override the implementation of the error event handler. We can provide a specific error event handler for that set operation. I'll show you what I mean. I'll use the set method and I'll set the color attribute to turquoise which will cause an invalid value according to our validate function. But now, I'll provide a second argument to the set method which is an object containing an error property which is function. So what I've done here is provide a custom error handler for this particular set operation. I will see in that, I'm just going to (inaudible) the error. So instead of the usual error handler which logs the error to the console, this time I'm going to display the error in an alert. So when we run that, turquoise is not a valid color according to our validate function, so the error handler is triggered and the message is displayed in an alert.
toJSON
ToJSON converts a model's attributes to a JavaScript object. The name is confusing. You might expect the toJSON method to return a JSON string representation of the model but it does not, it returns an object containing copies of the model's attributes. ToJSON is useful as a first step to serializing a model or when you just want to have a look at a model's state. The object returned from toJSON can be passed to JSON.stringify to get a JSON string representation of the model. Here, I've created a Backbone model object called the ford and set an attribute type with the value car. Using toJSON, I cannot extract the model's attributes and log that to the console. So what return from toJSON is an object. The attributes object contains my model's attributes. If I want to get a JSON string representation of my model, I can now forward the attributes objects to JSON.stringify which will convert the object to a JSON string. ( Pause ) So running that, I get this output here which is a serialized JSON string representation of my original Backbone model object. ( Pause )
save, fetch and destroy
Models have save, fetch, and destroy methods for synchronizing with the server. Save performs insert and update operations depending upon the state of the model. If the model is new and has never been saved to the server before, then save will perform an insert on the server. If the model is an existing model that has previously been inserted, then save will perform an update. The fetch method updates the model with the current server-side state. And the destroy method deletes the model from the server. The details of server integration will be covered in the later module of this course. ( Pause )
Summary
Models are the core of the Backbone application, they hold the data and they trigger a user interface changes by raising events. Models provide get and set methods to access their attributes. New models are created by using the new operator. When a new model is created, its initialized method is invoked if it has one. Models raise events when their attributes have changed. You can also raise custom events as required by your application. New models are automatically assigned a locally unique identifier when they are created. Once persisted, they also get a real id property issued by the server. A model can be configured with the set of default attributes. Models are validated prior to performing set or save operations, implement validation by providing a validate function. ToJSON converts a model's attributes to properties on a JavaScript object. Save, fetch, and destroy are model methods for synchronizing data with the server.
Views
Overview
Views of the interface in both directions between your HTML document and your Backbone models end collections. For most applications, views will form the majority of your Backbone js code. (Silence) The first thing we need to cover is the high level description of what views are and how they're used. Then we'll move on to defining custom view types and instantiating instances of views. (Silence) Views provide a number of useful properties so we'll investigate each of those. Most views need to generate or modify some part of a HTML document and that is the random methods responsibility. (Silence) Views are often backed by a model. We'll have a look at how that's done. Views receive events from models and from the HTML document.
Views
Backbone dot view and your types that extend it are used to bind models to the document. The Backbone documentation says the general idea is to organize your interface into logical views backed by models each of which can be updated independently when the model changes without having to redraw the page. Views often handle model change events as well as events raised from the DOM. In the diagram, the arrows between boxes show the direction of the dependencies. Views depend on models. Models trigger events that the view can handle. Views depend on the DOM and the DOM can raise events that the view can handle. New view types are defined by extending Backbone.view. As with models, you can create an inheritance hierarchy if you need to. All views have an associated DOM element at all times available in the .el property. The element is either passed to the view's constructor or it is created by the view. Some views create new DOM elements. Using the tag name, ID, class name, and attributes, Backbone will create a new DOM element for this view when the view is instantiated. Tag name is the name of the HTML element that will be created. ID is the elements ID. Class name is the elements class attribute. And attributes is a collection of other attributes to be added to the element. Here I'm defining a new backbone view type called V. The tag name is li so this view is going to represent elistodum (phonetic). Elistodum is going to have an ID of thing. It is going to have a CSS class of active. And it's going to have an attribute called data-value with a value 1, 2, 3, 4, 5. I declare a new instance of the view type the normal way by calling its constructor. And then I can use J query, select the document's body and prepend the view's element. When I run this code, you'll see in the HTML view over here a list item has appeared. And if we look at the markup that's been created, it's a li element. It has attributes of a data value, ID and class. Sometimes if you use an attached to a DOM element that already exists, the best way to do this is to pass the element to the view's constructor. Here's an example I've prepared earlier. I've declared some markup. I have a div with the ID test and the content -- test content. And then the code for this example declares a new view called V. It instantiates an instance of that view passing to the constructor an element selector of hash test so that's going to select the HTML element with the ID of test which matches the div that we've added to the page. Once the views been created, we can then access the .el property and use the CSS method to set the background color to cornflower blue and the result as you can see is that the background color of my div has been set to cornflower blue. So this is an example of attaching a Backbone view to a DOM element that was already a part of the document.
Instantiating Views
Views like models are created by calling their constructor function with the new operator. To create a view with no special behavior, we can instantiate an instance of Backbone.view. To define specific behavior for a view, create a new view type by extending Backbone.view. You can then create new instances of your newly defined type. Often you will pass a model to the view constructor. The model contains the data that is used to render the view. (Silence) There are a set of properties that if supplied to the view's constructor, will be copied to the view object. These properties are model, collection, el, ID, class name, tag name, and attributes. To demonstrate instantiating a Backbone view, I'm going to start by defining a new Backbone model. Now my model is an instance of the Backbone.model type so I haven't bothered to define my own model type. I'm just using the default Backbone type. And then I'm setting up a property called content to a string value. Having done that I can now create a new view. And again, I'm just going to use the view type that comes with Backbone. And when I create my new view, I'm going to provide a model object which is the model that I previously created and I'll set class name as well. Finally, using J query, select the document body and prepend the view's element. Let me run that. ( Silence ) And inspecting the rendered markup, we find there's a new div with the class model object. So that is the view object that we've created and added to the document.
The el Property
Next we're going to go through some of the properties of Backbone views beginning with el. El is a very important property that refers to the view's DOM element. Every view maps to exactly one DOM element that may or may not have been added to the document. (Silence) In this example, I create a new view passing a selector that will select the document's body. The selector can be anything that would be a valid J query selector. The element property of the view then refers to the document's body. Let's try that out. (Silence) Here I have the J is fiddled with -- I've added a little bit of custom markup. The same div that we've seen before with the ID of test. In the java script code, I'm instantiating a new backbone view and passing in a selector for the body of the document. And then logging the content of the view's element property or el property. If we run that and have a look in the console, we can see that the views el property is the content of the document's body. Another important property on the view is dollar el. Dollar el is a J query object containing the el property. It's therefore a J query object wrapping the view's DOM elements. Dollar el is cached to avoid repeatedly creating the J query object. This example is similar to the last one. The view is created with an el selector for the document's body. Once created, the view's dollar el property contains a J query object containing the document body. (Silence) This example is very similar to the last one. We have a test div. We've created a new view. We pass the body selector for the element, the views element. And then this time, we're logging the dollar el property of the view. So instead of that being the DOM element for the document's body, it's going to be a J query wrapper containing the DOM element of the document body. If we run that example. Go to the console. And see again, very similar to the output from the last example except that you see that there is actually an array containing one item. That's because it's a J query wrapper. Dot dollar is a version of the J query function, scoped to the current view. Using this dot dollar means that your J query selectors don't have to be unique for the whole page, just for that view. Using the dot dollar property is equivalent to calling the find method on the dollar el property.
render
Render is the function that renders the view's element usually based on the view's model data. The default implementation doesn't do anything. When you define your own view type, you should implement a render method that generates the markup you require. By convention render methods return this. This patent makes it easy to chain method calls. Generating markup for models and binding views to the models change events is the core of Backbone js. To set a view's model, pass the model to the view's constructor. (Silence) Often as in this example, an event handler is attached to the models change event and the view is rendered by the event handler. One model can provide the data for many views. If anything causes that model's attributes to change, all of the bound views are automatically updated. Let's think about a view that automatically refreshes when it's model's data changes. I'm going to define this view and call it refreshing view. (Silence) As usual, we extend from Backbone.view. I'm going to use the initialize method to set up the binding to the model. My views model can be accessed by looking at the model property, this dot model. And to bind to a change event, I use the on method. The name of the event to bind to and then the handler function. And within the event handler for the models change event, I simply want to call this dot render to render the view. I also need to pass the view object as an argument to the on method. Now what that does is it ensures that when my event handler function is called it sets the context for the event handler. Okay. So now I'm set up such that any changes to the views model will cause the view to be re-rendered. But I still haven't defined a render method so currently I have the default no-off render method. So the next step is to specify what it means to render this view. So I create a render function. Within that render function, I access my J query wrapper around the view's element. I want to replace the element's content so I use the HTML method from J query. I'm going to replace it with the model's text property. So when I pass a model to this view's constructor, I have to make sure that it has a text property. So that's it for the definition of our refreshing view. The next thing I'm going to do is create a new model. (Silence) And as I said before I have to make sure the model has an attribute called text. I'm going to set that to the current date. I can now create a view object so it's going to be a new instance of my refreshing view. I'm going to specify the model object to use to be the model that I just created. And I'm going to specify the element to be the document's body. And then render the view. (Silence) I've created a custom view and a new model. And my view is configured so that it will re-render itself when it detects a change to the model object. At this point, I can run this. And you see, it replaces the document's body with a string indicating the current time. To prove that this works and that it will in fact track changes to the model, I'm going to set up a timer using the set interval method. When the timer fires, I'm going to change the text attribute of the model to whatever the date happens to be at that time. And I want this to trigger every one second, one thousand milliseconds. I create a view. I apply a model, render the view. And then every second after that, modify the view's text property. When the view's text property is changed, it's going to trigger a change event. The view will handle that change event and re-render the document's body. So what I expect to see when I run this example is that the date printed in the document's body will update once every second (Silence) like so. So you can see the seconds are incrementing. ( Silence )
make
Occasionally, you will need a lightweight technique for generating DOM elements without using templates. You could manually build a string and have J query generate a DOM element for you or you could add a bit of structure using Backbone's make method. Adding a bit of structure is what Backbone is all about. The three arguments to the make method are the type of the element to create the attributes to set on the element, and the value of the element. In this example, the type of the element is h3. The attributes are a class attribute with the value, not very important. And the value is preliminary version. The comment at the bottom is the element that would be created. This is the example from the slides, h3 not very important class, and the value preliminary version. If I run this example, it is going to dump that element to the console and you can see it creates the HTML DOM element that we would expect. ( Silence ) Remove is a method that removes the view's element from the DOM. It's equivalent to calling J query's remove method on the Dollar el property. Calling a remove when a view is no longer required is an important first step towards preventing memory leaks. To demonstrate the view's remove method, let's start by defining a Backbone model. This model represents the data that we're going to render into a header and it contains a single attribute content with the value historical context. So the next thing we need is a header view to render a header. And it's a custom view extending from Backbone.view. It's going to render as a p tag also known as paragraph and in the render function, you're going to set the content of the view's element (Silence) to the model's content attribute. (Silence) Now to clear the instance of the header view (Silence) and provide my header model as data for the view. And with J query, I'll render the view and add it to the document body. Call the render function, access the element. If I run this code, it's going to create a view based upon my model and add it to the document. There it is. If we have a look at the markup, it's created a paragraph tag with the content, historical context. It's exactly what we were trying to do. Okay so now, I can use the view's remove method. If I run the code again, it's going to create that paragraph, add it into the document, and then immediately remove it. Like that so that the paragraph's gone. The remove function is just a convenient way to remove the view's element from the document.
events
Backbone views have a nice declarative syntax for specifying DOM movement handlers. It's very important to understand that Backbone views should be self-contained. Therefore, they only handle events that occur within their own elements. In this example, I'm declaring that the handleClick function should handleClick events on the elements within this view that match the selector dot clickable. Events is an object or a hash where the key is of the form event name, then a space. Then a CSS selector. So we're going to handle that event that occurs on those elements and the value side of each property is the name of the event handler function. If you wish and if things are simple enough, you can also just directly put the function in that position. Otherwise, you just declared the function somewhere else on the view. The previous declarative event declaration is equivalent to this imperative J query code. So we're doing the same thing within the current view. You use J query to select the elements that match the dot clickable CSS selector. And attach a handler for click events and the handler function is handleClick. (Silence) To demonstrate how Backbone views deal with DOM events, I'm going to create a new view called form view. And as in the previous example, we provide a property called events which is an object. The key on an event name followed by a space and then a CSS selector. And the value is the name of the event handler function. So let's start with that. So configure one event handler. I'll add a render method so that our view has some markup. I'm simply going to render a very simple HTML string. (Silence) So that render function is simply going to generate two text boxes. The first one has a placeholder containing the word clickable and it also has the CSS class clickable. The second one is just a plain vanilla text box. A previously configured event handlers to call a function called handleClick. So now I have to define that function on the view. And within the handleClick function, I'm just going to log a message with the name of the event handler. That's enough code now that I can create a new instance of that form view. And using J query, select the document body and append the result of rendering the form view. If I run that now, gets some output, you can see here it's rendered my two text boxes. The first one has a clickable placeholder and it has the CSS class clickable. The second one is just a vanilla text field. My clickable text box has an event handler configured such that when the text box is clicked, it logs a message called handleClick. As you can see in the console if I click that, every time I click it, it logs a message handleClick. If I wanted to add a second event handler, I add another entry to my events object. This time I'm going to handle the change event. I'm not going to specify the CSS selector so that's going to apply to everything. And I'll declare the event handling function in line just to show that you can do that if you want. Although if there's any sort of complexity within the event handler, this tends to be more difficult to read but for very simple cases I think it's okay. Once again, I think I'll just log a message, handle change so we can tell the two events apart. Render the page again. Once again, I still have the click handle on in this clickable text box. Every time I click in there, it writes the message handleClick. If I put some text in the text box and then move the focus away, that raises a change event and we get the handle change message logged. And the same is true of the second text box. Enter some text in there. Move the focus away and the change events fired again and the event handler runs and logs the message, handle change.
Guidelines and Summary
As I mentioned before, it's important that views be self-contained. If you don't, your program can become difficult to manage and testing will be a nightmare. Do not attach to existing elements other than the elements passed to the view's constructor. And above all, do not access DOM elements that are not part of the view. If your view is set up to refresh itself when its models is changed, make sure that the view's element is supplied to the constructor not generated by the view. If you don't do this then the view will regenerate its element when it detects a change but the element will never be added to the document so the update won't be visible. Views are the components responsible for mapping between models and the user interface. They render model's attributes to markup. They handle DOM events and they handle model and collection events.
Templating
Overview
So far all of the views we've created have rendered themselves by manipulating the DOM with jQuery. But that is a slow and frustrating process. For any substantial real world view, we need a more efficient way of generating DOM elements. The solution is templating. Client-side templating is the technique of using javascript in the browser to render markup by substituting values into a static template. Underscore.js is a dependency or backbone. It includes a simple yet powerful templating function. Handlebars is an advanced templating solution that includes logic list templates and template pre-compilation. Pre-compiling templates is the technique of transforming templates into executable functions ahead of time. Template compilation is the slowest part of the template rendering process so doing it service side as a build step is a substantial performance boost. Client-side templating is a technology that we use to dynamically build markup from a template and some data. The template and the data are typically both retrieved from the server and passed through the templating library to produce markup. Templating is something that nearly all backbone apps will use but backbone doesn't include any specific support for templating. You have the freedom and the responsibility to evaluate and choose your own templating solution. Backbone applications typically use templating in the view's render method.
Underscore Templates
Backbone doesn't include any templating support but it does depend upon underscore.js; and underscore.js has a simple but powerful templating function. Underscore templates consist of html and 3 different executable code blocks. The first allows the execution of arbitrary code. It's used to set variables, to loop over collections, or to conditionally branch. The second style of code block which prefixes the expression with an equal symbol evaluates the expression and renders the results inline. The third style of code block which prefixes the expression with a hyphen evaluates the expression and renders the html escaped results inline. This is an example view with the render function that uses a client-side template and the underscore template method to render the view. This is the render method. The first line declares an object containing the data that the template will use. The next step is to set the html content of the views element. The content is generated by calling the underscore template function. The first argument is the template. It's a string containing html and some code blocks. The second argument is the data. The value returned from the template function is a string. The example we just saw in the powerpoint declared a new view called V. Within that view, it set the view's element to be the document's body. And there was a render function. Within the render function, we declare the data that will be used to render the template. In this case I'm going to have 2 values. One representing a latitude and the other representing a longitude. Then to perform this view's render operation, I want to access the jQuery element object and replace its content with the result of rendering the template. So I'm going to call the underscore template function. I'm going to provide the template inline as a string. This is the underscore code block that evaluates the expression and renders the result inline. Needs 2 of those code blocks, one for each of our variables. The second argument to the template function is the data to use. As usual, we'll have the render function return the view. So having defined that view, I can now instantiate a new instance of the V view. And then tell it to render. If we run this example, (pause) the output is a page containing my data values. So the result of rendering the template. A more realistic scenario might be to move the template out of the view's render function and into a script tag within the DOM. We can do that by placing the template text within a script tag and using a type value that's something other than text javascript. Anything other than javascript will prevent the text from being evaluated by the browser's javascript engine. I can now modify the render function to read the template out of the document. And just do that using jQuery and selecting the script tag by its ID. Now we get the content without a script tag. We can then take the string literal template, delete that, and replace it with the template variable. Another improvement we can make here is to remove the element specification from the view and instead pass it to the view's constructor. This is a simple change but by designing views in such a way that they don't directly access the page document object model, it makes the views much easier to test because I can now create an instance of this view and work with it without ever actually adding any DOM elements into a page. I'll just run that once again. Now it's rendering the template by reading the template text out of the document and producing a slightly different result matching the template that I've defined. Underscore templates script blocks can contain any javascript statements. This example uses underscore to loop over an array and output each value in a p tag. I'll go back to the example we were just working with. I'm going to modify the template. Now as well as rendering the latitude and longitude in paragraphs, I'm using underscore to iterate over an array using the h function. The array containing the integers 1, 2, and 3. Each time through the loop this variable number will be set to one of the numbers from the array and then the bit of underscore template renders a paragraph tag and the current number. If we run the example now, you'll see it's added 3 paragraphs containing the numbers 1, 2, and 3. Vertrial (assumed spelling) templating requirements, it's common to store the templates inside the script elements. Using a non-javascript type attribute prevents the browser from attempting to execute the template as javascript code. When the template is required, jQuery can be used to read the text content of the script tag. Typically the template would then be compiled before use.
Handlebars Templates
Handlebars is a templating library based on another templating library called mustache. Handlebars templates can execute a small set of simple constructs like conditionals and loops. Anything more complicated is achieved through helper methods. The authors believe that views should be as simple as possible and should not contain arbitrary code. This is believed to properly separate the view from the application code. Code blocks are delimited by double curly braces. Hence the name mustache, and then handlebars for the enhanced version of mustache. To insert values inline requires wrapping the variable in double curly braces as we see here for the lat and lon variables. Each is an example of a handlebars helper. It contains markup between its start and end tags. The contained markup is rendered once for each item in the collection. Within the contained markup, this refers to the current item from the collection. Rendering is a two stage process. First, the template is compiled. Then it can be executed. Handlebars supports compilation via the handlebars.compile function. Its input is a template string. Its output is a template function. The second step is to execute the template function. Its input is the data required to render the template. Its output is the string of html. To render handlebars, the first thing we need is a template. So here I have a simple handlebars template containing one variable substitution. And I've assigned it to a javascript variable called source. The next step is to compile the template and that's done using the handlebars compile function. Once we've compiled the template, we can then render it. And at this point, we have to provide some data. So for this template, it needs a variable called lat. And for this demonstration I'm simply going to write the rendered html to the console. If I run that, you'll see it renders the p tag containing the lat variable. The step where we compile the template from a string source to a template function is the most expensive part of this project, performance wise. So if you're going to be doing this sort of code in your application, you want to make sure that you only compile each template once and then you cache the template functions and then you can reuse them as many times as you like.
Precompilation
Handlebars requires and underscore allows templates to be compiled. Compiling a template means converting the template string into a javascript function that can be executed. To compile an underscore template, simply call the template function passing the template source but not the data. Compilation is the most expensive part of template rendering so it makes sense to compile your templates as a build step so it doesn't need to be done at run time. It's possible to write a script that will find all of your templates, compile them, and combine the result into a single javascript file. This demo is going to show how to pre-compile templates with underscore.js. The first thing we need is a template string. I'm going to use the same example that we've been using up until now. A template that renders two variables, lat and lon separated by a space. You can then compile that using underscore's template function and passing my template string as the argument. If we then logged the type of the compiled template, you see that it's a function because what the template compilation process does is convert a string template into a function that we can call to render the result. And we can even have a look at that function by logging it to the console. ( Pause ) That's the function that underscore generates for this template. Once I have a compiled template, the hard work has been done for me. So I could now if I choose go write a loop. And within that loop I'm going to render this template multiple times. We'll be looping a variable i from the value zero through to the value 99 incrementing by 1 each time through the loop. We're going to log the result of rendering our template. ( Pause ) If I run the example, it goes ahead and renders the output 100 times. My favorite feature of handlebars is its support for pre-compiling file-based templates. This is done using the handlebars command line executable and specifying the input files containing your templates, and an output file to write the compiled templates to. The pre-compilation should be triggered by a file system watcher, a build script, or a post-build event if you are working with visual studio. For the handlebars executable to work, you must have note.js and the handlebars mpm module installed. Installation instructions are on the handlebars website. Note can be installed or available locally. Let's try an example. Here I have a simple web application consisting of a html file and some handlebars templates in this templates directory. Looking inside the templates directory, you can see it includes a subdirectory that also contains templates. This is important because we want to be able to organize our templates in a logical way that matches our app. And to do that, we need to be able to distribute them into directories and subdirectories and sub-subdirectories and so on. Opening each of the templates, you can see that it is a handlebars template. The html file is an empty boilerplate with reference to a single script. The single script is handlebars.runtime.js. Because our application will use pre-compiled templates, we don't have to reference the full handlebars script. Handlebars.runtime is a cutdown version that omits the handlebars compiler. The next step is to pre-compile the templates. To pre-compile the templates we need to switch to the console. And then use the handlebars executable. The first command line argument is a reference to the location of the templates. In my example, the templates are in a directory called templates, and in subdirectories of that directory. Handlebars is smart enough to know how to recurse the subdirectories. The second argument I need to specify is the file that the compiled templates will be written into. Because the compiled templates are javascript functions, this file is going to be a javascript file. And now I can run that, and it's done. If we have a look, we can see that a new file has been added, templates.js. And within that file, a whole heap of javascript containing all of my handlebars templates converted into javascript functions. Back to the web page, I need to add a reference to my compiled scripts (pause) which you remember were in the file, templates.js. Now that I have compiled templates, I can add a javascript block and start rendering templates. The list template renders a list of people so let's start with that. So first up, I want my rendering to happen once the DOM has finished loading. So I use javascript to do that. I can specify some data; and the list template requires a single variable called, people. And people is an array. And each item in the array, I'll make a person's name. ( Pause ) I can now render my pre-compiled handlebars template. When you compile the templates, they're attached to the handlebars.templates object. And the name of the template is the name of the file that was compiled. In this case, list. And the data is going to be my javascript object, data. So that's going to give me back the rendered markup and then I'll use jQuery to add that into the page. ( Pause ) And oops, it doesn't work. It's telling me that dollar is undefined. Dollar being the jQuery function because I forgot to include a reference to jQuery. So I go back to the web page. I'm just going to add a reference to jQuery on the google cdn. Let's try that page again and now it works fine. So in rendering my list template, it's taking each item in the data array that I provided and rendering a list item. And if we have a look at the source, it matches the handlebars template that I began with.
Summary
Templating is a core dependency for client-side applications. Nearly all client-side applications all require a templating library and the one you choose will become an important part of your project. Backbone depends upon underscore. So using underscore templates is an easy way to get templating support without having to take any additional dependencies. Handlebars supports semantic templates meaning templates that don't include code and it has a powerful pre-compilation tool. Pre-compilation is essential for any real world application. Either use something like handlebars or write your own script to find all your templates and pre-compile them for you.
Routing
Overview
Routing is a part of Backbone.js that causes a lot of problems and confusion. The key to mastering routing is to understand exactly what it is and isn't and not to get it confused with the kind of routing you may have seen in server-side MVC frameworks. Client-side routing means responding to URL changes in client-side JavaScript code. The way Backbone does it, we start by defining routes. For Backbone a route is a combination of a URL pattern and a function to call when the URL is changed to match that pattern. Backbone is capable of triggering a route by changing the browser's URL and invoking the appropriate route handler. Modern browsers, other than Internet Explorer, support the HTML5 history API. Using history, Backbone can modify the URL for client-side routing. For browsers that don't support the HTML5 history API, Backbone can only modify the hash fragment for routing. The hash fragment is the part of the URL following a hash. Client-side routing and client-side applications have implications for search engine indexability. If your user interface is generated client-side by JavaScript, your content will not be automatically indexable by Google and other search engines.
Client-Side Routing
Client-side routes are a way to change the browser URL and to trigger a function when the browser URL changes. Backbone routing includes parsing of the URL to extract route tokens and matching the URL to the correct route handler. Client-side routes are used very differently to server-side MVC routes. In particular, it's not necessary for every user action to trigger routing. Use routing only when you need to substantially change the application context, to generate a bookmarkable URL or to generate browser history that can be navigated backwards and forwards. Each route results in two different scenarios that must be accounted for. We'll look at those next. Routes can be triggered in two different ways. The first is when the browser initiates the request because the user enters an address in the address bar, refreshes the browser or clicks a hard link. In this case, the request begins at the browser. The browser initiates a request to the server. The server will process the request like any other and return a response. When the response has been loaded in the browser, the Backbone router will try to match it to a route. If a match is found, then that route's function will be called. That's the process that occurs when the request is caused by the user's interaction with the browser. The other thing that can trigger a route is a client initiated request. When your client-side application undergoes a significant state transition, such as changing from one page to another, you can tell the router to update the URL and optionally trigger routing. Doing so does not trigger a request to the server. This time the progression is: Backbone updates the browser URL and initiates routing. The Backbone router tries to match the URL to a route. If a match is found that route's function is called. Much of the complexity in dealing with Backbone routes is in accounting for these two different routing scenarios. Don't use routes unless you want to change the browser URL to provide an addressable state or a potential bookmark.
A Document Router Demo
By way of demonstration, let's consider the process of building a document router. Our requirement is that given a set of documents, each having a title and some content, when the user selects a document, then the selected document is displayed. I'm starting with an empty page that includes the scripts required to work with Backbone.js. Our requirements begin with a set of documents. In JavaScript we will model that as an array called documents. Each document is an object containing two properties, "title" and "content". ( Typing ) The document objects are going to be view models. Let's convert them to Backbone models. ( Typing ) In our requirements, a user selects a document. So we're going to need to display a list of all the documents. To do that let's define a view called "ContentsView". Contents is a list of documents. So it makes sense for it to render as a UL element. To render the contents view, we'll iterate over the documents and add each to the view's element. ( Typing ) Now I realize that I need a view to represent an item in the list. It needs to render a list item representing the document. ( Typing ) The new view is called "DocumentListView". It renders a document as a list item. The content of the list item is the title of the document. Now I can update the contents view to render the list of documents. ( Typing ) We still need an entry point to the application to render the initial table of contents. Let's define our application router. We need to route to initialize the table of contents. ( Typing ) The contents route handler replaces the body of the page with the rendered contents view. The contents view gets its data via the collection property which is set to the documents array. When we're ready to start the application, we can do so by navigating to the contents route. To do this we need a reference to the router. So we need to create a router instance... and start Backbone listening for URL changes. ( Typing ) That's enough to get the table of contents rendering. So let's open this application in a browser and try it out. The application starts by navigating to the contents route. The route handler replaces the page's body with the contents view, which displays the title of each document. The contents render, but I still can't select a document to view. To do that I need to add an event handler to the document list view. When the list item is clicked it should trigger route navigation to a route matching the document's title. ( Typing ) The view does not have a reference to the router to do this directly. So instead it will raise an event via a global event aggregator. An event aggregator can be defined by mixing the Backbone.Events module into any other object. ( Typing ) Now the list item to click event handler can trigger an event on the event aggregator and forward the associated model. The event is called document:selected. We'll need a new route on the router for navigating to documents. ( Typing ) The new URL path is the word "view" followed by a forward slash followed by the title of the document to view. When this route is triggered, Backbone will invoke the viewDocument function. Within the view document function, the first statement searches through the documents array to find the selected document by matching its title to the title from the URL. ( Typing ) The second statement uses jQuery to clear the document body and then append the result of rendering the document view for the selected document. DocumentView does not exist yet so let's create it. DocumentView exists to display a document, including its title and content. The view's model property will be a document model. ( Typing ) The first statement in the document view's render function creates a new h1 element containing the document's title and appends it to the document view. The second statement creates a new div element containing the document's content and appends it to the document view. Selecting a document in the list triggers the document:selected event, but nothing is listening to that event. Let's add an action now that will run when the document:selected event is triggered. Remember that the selected document model is passed with the event. ( Typing ) In the document selected action, we want to navigate to the document's URL path. To navigate to the document's URL path we need to build the URL... and then navigate using the navigate method. If I reload the application in the browser, the application immediately redirects to the contents URL path, which causes the table of contents to be rendered. If I click on one of the documents in the list, Backbone redirects to our document view URL path and renders the document. The title is rendered in a h1 element, and the contents is rendered in a div. If I navigate back, using the browser's back button, it redirects me back to the table of contents URL, which causes the table of contents to be rendered. If I select the other document, then I'm redirected back to the document view.
Defining Routes
Define routes by defining a type that extends Backbone.router. Each property on the object passed to the extend method pairs a route pattern to a route handling function. If the URL matches the route pattern, the corresponding route handler will be invoked. The route pattern can include parameter parts prefixed by a colon, which will be matched against the URL and passed to the route handler. In this example, "query" is a parameter part. The router definition must also include the route handlers themselves. In this example, "search" is a route handler function. Note that the parameter named "query" matches the parameter part to find in the route expression. If the URL was "search/cats", the route pattern would be matched, the search function would be invoked and the query parameter would have the value "cats". Navigate is the Backbone function for updating the browser's address and triggering routing. This is how we trigger routing from client-side JavaScript. Calling navigate is purely client-side operation. It does not trigger our request to the server. It does not reload the page or clear any state. It simply changes the URL and optionally invokes a route handler. When calling navigate, the first parameter is the new URL path. The second parameter tells Backbone if it should trigger routing. Define a new router by extending from Backbone.router. Routers are optional, but a typical Backbone application will have one, perhaps many. The object passed to the extend method is given a route's property. The name of each property on the route's property is the URL pattern for that route. The value is the name of the function to call when that URL pattern is matched. In this example, I've defined a single route. The route's URL pattern begins with the word "search" followed by a forward slash and then the route parameter part "query". This URL pattern is paired with the function named "search". The next step is to define the search function as a property of the object passed to the extend method. The search function will have a single argument corresponding to the single route parameter part in the URL pattern. In this case, "query". For this example, when this route pattern is matched and the search function is invoked, I'm going to have it log our message to the console. The message logged will include the value of the query route parameter part. We have just defined a Backbone route type called "Workspace". To use it we must instantiate an object by calling "new Workspace". Once all of our routers have been instantiated, we can tell Backbone to start listening for address changes by calling Backbone.history.start. We can trigger client-side navigation by using the navigator function of the router object. The first parameter is the new URL path. The second parameter is an object containing a property that tells Backbone if it should trigger the routing process. If this value is false, then Backbone will just silently change the browser's address. In this example, we have defined a new router type called "Workspace". This router type has one route with a URL pattern search forward slash and then a query parameter, and it matches that URL pattern to a function called "search". Once we create an instance of our router type and tell Backbone to start listening for changes, if the URL path changes to something that matches our URL pattern, then the search function will be invoked. The final part of this example uses the router.navigate function to change the browser's URL path to a value that will match the URL pattern causing our search function to be invoked and a message to be written to the console. Let's see if that works. The output is "searched for cat", where "cats" is the route parameter part substituted from our new URL path. If I was running this example directly in a browser instead of inside a frame in jsFiddle, you would have seen the browser's URL change to this URL path.
PushState and Hash Fragments
The HTML5 history API introduced a way to change the browser URL without reloading the page, which is how Backbone's navigate method works. HTML5 history is a relatively new technology and many browsers do not support it. Caniuse.com have a compatibility table that shows the details. If I search for pushState, I get a table that shows the browsers that currently do and don't support the pushState technology. The first thing that jumps out at me is IE 7, 8 and 9 are all marked as not supporting pushState, which is sad because for most of us that's a fairly large part of our audience. Firefox looks to be okay after 3.6 and all of the Chromes shown here are fine. Backbone automatically detects HTML5 history support and falls back to hash fragments if it's not available. Prior to HTML5 history, JavaScript could only modify the URL by appending a hash followed by a string, that part of the URL known as the hash fragment. If a request is made to a non-hash fragment URL and Backbone detects that the browser only supports hash fragments, then Backbone will redirect to the hash fragment version. The highlighted section of the URL is the hash fragment. The big difference between HTML5 history pushState and hash fragments is that hash fragments are not sent to the server. If the browser requests this URL, the server will receive a request for the full URL. However, if the browser requests this hash fragment URL, the server will receive a request for the URL up to but not including the hash character. The hash character and everything that follows it will not be part of the request. Both of these requests will trigger the route.
Search Engine Indexability
When a search engine spiders a webpage, it indexes the results of its request. In a standard Backbone application, the result of any request is an empty application shell. The content is added later by JavaScript. Markup that is rendered on the client will not be indexed by search engines. One solution is to have the server render the initial page content. The downside is that you have to duplicate your rendering logic, once on the server and once on the client. Also, this will not work for those browsers that do not support HTML5 history. Another option is to use #! URLs by disabling HTML5 pushState and beginning routes with an exclamation mark. Google have an AJAX crawling specification that knows how to transform #! URLs into a format that can be sent to the server. Your server application is then responsible for responding to the alternative URL format and returning a page that includes your content to be indexed. So either way, you need to be able to render your content on the server. Google's #! specification is largely obsolete now that the HTML5 history APIs exist. Simply use proper URLs and render content on the server if you require search engine indexing. All the browsers will convert the URL to its hash fragment equivalent and trigger the route handler that way.
Summary
Trying to use Backbone's route handlers like you would use controller actions is a common mistake made by people who have experience working with the model view controller pattern. Much of the responsibility of MVC controllers is moved to the view in Backbone so mapping controllers to routes does not fit. The responsibility of the router is to update the browser's URL and to respond to changes in the browser's URL. Use with caution. Routes are defined by pairing a route pattern, including route parameters, with a route handler function. When the browser URL changes, the route handler will be invoked if the URL matches the route pattern. Modern browsers that aren't Internet Explorer support HTML5 history and pushState. This gives JavaScript the ability to update the browser's URL, not just the hash fragment. Older browsers and Internet Explorer can only manipulate the hash fragment. So URLs will look a little different.
Collections
Overview
There's only one thing better than the backbone model and that's a collection of backbone models. Collections group related models together at a convenient way to persist them and provide a set of helpful functions for dealing with sets of models. Just like models, a collection can be used as the basis for a view and binding a collection change event to a view's render function will automatically keep them in sync. When there is a set of models of the same type that you want to group together, use a collection. Collections have sorting capability that not only allows you to sort a collection but also to keep the collections sorted as models are added and removed. When working with collections, you are definitely going to want to move models in and out of the collection. There are many different ways to access models in a collection, the most basic being array-like random access. Backbone collections have 28 iterator methods. The things like funding a model, reducing a list, mapping a list, sorting, grouping, and shuffling. Like models, backbone in collections published a number of events. There are events from model added, model removed, model changed, model destroyed, collection synchronized, collection reset and validation error.
Collections
A backbone JS collection is a container for storing a set of related models. Collections, like models, have a URL property and it can be used to retrieve a set of models from the server or to create a new model on the server. Collections group related models. For example, you might divide your models into old and new categories and have a collection for each. Collection is an array-like object. Like an array, it has a length property that tells you how many elements it contains. Also, like an array, it can be indexed although the syntax is different to an array. You can't index a collection using square brackets. You must use the 'at' method. A new collection archive can be created by calling new Backbone.Collection. The collection can be populated by passing an array to its constructor. ( Pause ) Having created a new collection, we can now log its length property. ( Pause ) The length is two because I've added two objects to the collection. And I can use the 'at' method to index into the collection and log the first item in the collection. ( Pause ) The first item is a backbone model containing a property called name with the value 'thing', which is the first object that I added to the collection.
Defining New Collection Types
By now, you will have noticed the pattern that backbone uses for defining new types. As with models and views, to create a new collection type we call the extend method on the backbone base type, in this case, Backbone.Collection. It's common to specify the type of the models that the collection will hold. In the example shown here, I create a new collection type called vehicles by extending Backbone.Collection. I declare that the models contained within this collection will be of type of vehicle. Let's start by defining a new model type called vehicle. And now I can create a collection type called vehicles which will hold a set of vehicle models. ( Pause ) And now I'll instantiate an instance of the vehicle's collection and provide some vehicle objects to the constructor. ( Pause ) I can log the collections length to inspect how many elements it contains, currently two. And I can also log the result of serializing one of the models contained within the collection. Access an element within the collection by using the 'at' method. ( Pause ) So that logs the serialization of the first element in the collection or I could serialize the entire collection. ( Pause ) Like models, collection types can have class properties. These are properties available directly on the type rather than on instances created from that type. To add class properties to a collection type, pass them as the second argument to extend. ( Pause ) Here we have our previous example. It starts by declaring a new backbone model type called vehicle and then declaring a new backbone collection type called vehicles which will hold models of that vehicle. To add a class property to the vehicles type, I need to pass a second argument to the extend method. ( Pause ) The second argument is an object and each property of these objects becomes a class property of the collection type. So I'm going to add a property called oneVehicle and this is going to be a function, that when this function is called, it will return a new vehicle model. ( Pause ) Now I can use that class property without creating an instance of my collection type. I can call it directly on the type itself. So we can see what that does. I'll log the result to the console and run the example. So the oneVehicle function has returned the vehicle model as defined.
Sorting
Collections are sorted. By default, the items are sorted by the ordering which they were inserted or they can be sorted by a comparator function. The comparator is a function that knows how to order your models. If you define a comparator, then the items in your collection will always be correctly sorted according to the comparator. In this example, models in the vehicles collection will be sorted by their sequence attribute. ( Pause ) This is a standard vehicle model and vehicles collection. To make these collections sorted, I add a comparator function. ( Pause ) This is a function that has a single argument which is a model of the type that the collection holds and it returns some value based on that model and backbone will sort the collection b y those values. So in this case, my comparator function returns the sequence attribute of the vehicle. The vehicles in the vehicles collection will be sorted by their sequence. That's the first type of comparator function with an input which is a single model of type contained in the collection and an output which is the value to sort by. Now if I instantiate a collection of vehicles and provide some data-- ( Pause ) -- and finally, log the contents of the collection. ( Pause ) What we see is that even though the vehicles were added, red first and then blue, because we're sorting by sequence the output has reversed the order and the blue vehicle is in the first position and the red vehicle is in the second position. This is the first type of comparator function. Takes a single argument which is an object of the type that the collection holds and it returns some value to sort by. There is a second type of comparator function that you can use. The second type takes two arguments which is two objects of the same type that the collection holds. The function should return minus one if the arguments are already in the correct order. That is vehicle1 is considered to be less than vehicle2. It should return 0 if they compare the same and it should return positive 1 if the order needs to be reversed. That is if vehicles2 is considered to be less than vehicles1. For this example, I can implement by sequence comparison like so. ( Pause ) Now when I run this example, once again, the order of my collection has been reversed so that now they're ordered by their sequence number. ( Pause )
Instantiating Collections
Backbone collection types are JavaScript constructor functions. Therefore, to create a new collection object call its constructor function with the 'new' operator. If you don't need any special behavior, you can directly instantiate an instance of Backbone.Collection. Usually, you'll be instantiating custom types. Vehicles is a custom collection type. Fords is a new object created from the vehicles collection type. ( Pause ) When you create a new collection, you can pass an array of model data to the constructor. If you included an initialized function, when you define the collection type, then it will be invoked after the constructor is called. The simplest way to get a new backbone collection is to create an instance of the backbone collection type. ( Pause ) If we log that new object, you can see it serializes to an empty array. We can also create a custom collection type by extending Backbone.Collection. ( Pause ) And then create an instance of that new collection type. If we want, we can pass model data to the constructor function as an array. ( Pause ) Define a model object. Just give it some data. Add a second model, some different data. ( Pause ) Now I log that collection that contains two model objects and it serializes again to an array containing two objects and the two objects match the two backbone models that I pass to the constructor. ( Pause )
add() and remove()
The collections add and remove functions are self-explanatory. Pass a model to the add function and it is added to the end of the collection. Pass a model to the remove function and it is removed from the collection. Use the 'at' option to insert a model at a specific index and the 'silent' option to suppress the 'add' event. When adding or removing models, you can choose to add or remove a single model or an array of models. Now, I'm going to demonstrate how the collections add and remove methods work. Start with a new collection. I'm going to add an object to that collection ( Pause ) The object I'm adding to the collection is a backbone model. Let's take a look and see what the collection looks like at the moment. I've added one backbone model to my collection. When we inspect the collection, you can see that a collection of objects containing one object with my data. So that's the basic form of the add method. It's not necessary to add backbone models. I could also have added a plain JavaScript objects version of the data. And in this case the collection will transform that object into a backbone model for me automatically. If I run it now, I'll get exactly the same output. Sometimes you have more than one collection that you'd like to add at one time. So the add method also will accept an array of objects or backbone models. ( Pause ) So now, I have a new collection to which I've added a single object and then I've added an array containing two backbone models. If I inspect the collection at this point, obviously I expect to see a total of three models in the collection. So observe that there are three models in the collection and they're ordered in the order that they were added to the collection, Fred then Sue then Dave. Once again, it's not necessary to add backbone models. I could also have just directly added JavaScript objects like so. I get the same result. If I want to remove something from my collection, I'll use the remove method and I pass the model that I want to remove. ( Pause ) In this case, I'm indexing into the collection and I am telling the collection to remove the model at position 1. Indexing into a collection is zero-based, so 1 is the second model in the collection which in this case is Sue. ( Pause ) Because I removed the Sue model, there're now only two items in the collection, Fred and Dave. When model is added to a collection, it triggers the 'add' event. So now I'm adding an event handler that's going to listen for 'add' events. Within this event handler, I'm going to log a message and the message is going to tell me the name of the model that was added and also the index in the collection at which it was added. ( Pause ) If I add another model to the collection now, I expect it to trigger the on event and call my event handler function. And that's exactly what's happened. So my event handler has logged the fact that I've added a model with the name Troy and it's telling me that it's been added at index 3. The reason it's been added at index 3 is because there's already three items in my collection, Fred, Sue and Dave. So the new model is going to be fourth item and the fourth item will have an index of 3. If you want to get a little bit more advance with it, we can pass a second parameter to the 'add' method and provide an 'at' value. This is instructing the collection to add the new model at index 3. Given the number of models currently in my collection, if I admitted the second parameter, backbone would insert the new model at index 4, which is the new end of the collection. So in this case, I'm saying insert my new model at index 3 which will push the item that's currently at index 3 up to index 4. ( Pause ) So when this last model was added to the collection, you see it was added at index 3. If I want to add a model and not have it trigger the 'add' event, I can pass a silent option with the value true. And that's going to stop this add from triggering the 'add' event. ( Pause ) The addition of the Troy model triggered the 'add' event and was logged, but the addition of the Eric model still happened but it didn't trigger the 'add' event so it didn't get logged to the console. ( Pause )
at()
'At' is a function that provides array-like random access to models in the collection. Like arrays, collection indexes are zero-based. That is the index of the element in the first position is zero. Therefore, the index of the last element in the collection is the length of the collection minus 1. Let's begin with a new collection object containing three model objects. ( Pause ) Using the collections 'at' method, I can retrieve any model object in the first position in the collection. The first model is Fred and that's the model that's returned. If I wanted to get the last model in the collection, again, I can use the 'at' method and retrieve the model at position length minus 1. ( Pause ) The last model in the collection is Dave.
get() and getByCid()
Get and getByCid are two functions for retrieving a model from the collection by its id and Cid properties respectively. Get retrieves by the model's id property. getByCid retrieves by the model's Cid property which is necessary if the model has not been saved yet because until the model is saved, it will not have an id property. Cid is an abbreviation for client identifier. You may remember from the models module that all models have a unique Cid assigned to them when they are created. They don't receive an id property until they are saved to the server. ( Pause ) I'll start with a standard collection containing a single model. I mentioned that all models have a Cid property from the moment that they are created. If I inspect the single model in my collection at the moment, it has the Cid c0, which is the first Cid that's assigned. Now that I know the Cid of my model, I can use that to retrieve the model from the collection using the getByCid method. ( Pause ) getByCid given the model's Cid, c0, returns that model. If I change that Cid to something else that doesn't exist, I get undefined.
Working with Collections
Underscore JS has a set of collection functions which are proxied by Backbone.Collection. forEach is among the more commonly used collection functions. It's used to execute a function once for each item in the collection as an alternative to suing JavaScript 'for' loop. In the example shown, the print function is called once for each item in the collection. An equivalent form is to pass the print function directly as the argument to the forEach function. Map is another popular function. It's used to produce a new array of values by mapping each item in the collection through a transformation function. For this reason, it's important that the function passed to map return a value. Here I have a standard backbone collection containing t6hree model objects. I'm going to use this collection to demonstrate some of the different collection iterator functions. The first is forEach. The argument to forEach is a function that will be executed once for each item in the collection. Within my forEach function, I will log the models name property. ( Pause ) So this bit of code is iterating through the collection. For each item in the collection it calls this function and logs the model's name, Fred then Sue then Dave. Map is used to transform the items in the collection from one form to another. Given the collection that I have here, I can use map to create a collection of my model names but converted to uppercase. Map returns a new collection, so we have to capture that. ( Pause ) So the map function has been executed once for each item in the collection and for each item it calls the map function. The argument to the map function is the current model and the map function returns some value derived from the model and then all of those values are collected into an array. So my input array of three backbone models has been converted to the name properties of the three models in uppercase. A more general iterator function is reduce. Reduce can be used to derive most of the other iterator functions if you like. What it does is iterate over the models in a collection and produce some kind of aggregate results. So for this example here, I could use the reduce function to calculate the sum of all of these people's ages. I'll start by defining the account variable that's going to hold the running total of ages and I declare a variable to hold the results which will be returned from collection.reduce. The arguments to the reduce iterator function are memo which is the running total and item which is the current collection item. The reduce method takes two arguments. The first is the iterator function and the second, which is optional, is the starting value from memo. So in this case, I'm going to use start. When doing a reduce, the iterator function needs to return the current total value which is then becomes the input to the next iteration. Since I want to calculate the total age, I can return memo, so that's the running total so far. The first time through that's going to be zero 'cause that's my starting value. I'm going to add to that the age attribute of the current item. So that's one way to calculate the sum of a particular attribute of the models in a backbone collection. The result of that is 109 which is the same as 6 plus 29 plus 74. ( Pause ) One final iterator function that I want to show is 'find'. Find is used to search for a single model within a collection. Find takes a single argument which is the iterator function, and this function needs to be a predicate. So it will return true if the current model matches what we're searching for, otherwise it will return false. And the first time it returns true, that model will be returned as the result of the find method. For example, in case, if I want to find the first model with the name Dave, I could use the find method and provide a function that returns true when the model's name is equal to Dave. ( Pause ) So collection.find is going to iterate through the collection and execute the iterator function once for each item. The first time that the function returns true, that model will be returned as the result of the find method. You can see that the result has successfully found the Dave model. ( Pause )
Collection Events
We can detect when a model is added to or removed from a backbone collection by listening for the add and remove events. As always with backbone, event handlers are attached by using the 'on' function. When an add or remove event is triggered, the handler is passed the model being added or removed and the collection. To demonstrate the collection's add and remove event handlers, I start with an empty standard backbone collection. The next step is to create a backbone model. And now I'm going to configure the event handlers for the collection using the 'on' method. First stop is the 'add' event. When a model is added to the collection, I want to log a message to the console. And the content to the message will be the serialized model and the word 'added'. ( Pause ) If I then use the add method to add the model that I've defined to the collection, I expect it to trigger the 'add' event and therefore invoke my 'add' event handler. So in the 'add' event handler, it's logged the serialized model and told me that it's been added. Now I can add a second event handler. Again, using 'on', and this time I'm going to listen for the remove event. And this event is fired whenever a model object is moved form the collection. When the models removed, I want to log. Again, this serialized model and this time the word removed. ( Pause ) And then I'll use the remove method to remove my model from the collection. So this example, I know when I run it, it creates a collection, it creates a model, adds a model to the collection and triggering the 'add' event and the 'add' event handler and then removes the model from the collection, triggering the 'remove' event and the 'remove' event handler. ( Pause ) So the output is we see the model being added to the collection and then the model gets removed from the collection. Both the 'add' and 'remove' events are firing and being handled. ( Pause ) It would be annoying to have to attach handlers to every model in a collection in order to detect the changes to those models. Fortunately, you don't have to. Collections forward model change events, so the easy way is to listen to change events on the collection. We know the model that triggered the events because it is passed to the handler. ( Pause ) Given an empty backbone collection, I can attach a change event handler. This change event on the collection is going to be fired whenever a change event is fired on any model in the collection. Within my handler, I want to log the model that was changed and a string telling me that it was changed. Now if I create a new backbone model-- ( Pause ) -- and add it to my collection, then I'll set a property on the model. And because that's a change to the model object that's in my collection, I expect the models change even to fire and then subsequently the collections change event to fire and be handled by my event handler. So that's what's happened. The change event on the collection has been handled and I've logged the serialized model. The same thing also works for the attribute change events. I can listen to change events for any specific attribute. So in this case, I'm going to add a handler for changes to the name attribute. ( Pause ) When the event is handled, I'm going to log a message just saying that the name property changed. If I rerun my previous example, when it comes time to setting the age attribute to 57, that's a change to the model, so the collection change event will fire but it's not a change to the name property. So the name changed event handler won't fire. We see the same result as previously. However, if I change this-- so instead of setting an age attribute, we're setting a name attribute. Set the name to Jimmy. Now when the name attribute is set, that's going to fire the collection change event and also the change name event. ( Pause ) So the first event, we see handled is the change name event. Runs the handler and informs me that the name property has been changed. And then the collection change event is also fired. ( Pause )
Summary
Collections are handy containers for working with groups of model objects. To define a new collection type, extend Backbone.Collection. By supplying a comparator function for your collection, you can ensure that your collection remains sorted by whatever algorithm you require. Use add and remove to move models in and out of the collection. Use get to get a model from a collection by its id. Use getByCid to get a model by its Cid. Use the iterator functions forEach, map, reduce, find, et cetera to do interesting things with collections. Collections publish events for model added, model removed, collection synchronized and collection reset. In addition, collections forward model events for model change, model destroyed and validation error.
Connecting to a Server
Overview
While backbone does not require a server, most people will use one. First to serve their application to the user's browser, and then to synchronize model data changes. Backbone implements the client's side of a simple RESTful protocol for persisting changes to model data. The service side of the protocol is easily implemented using your service side web framework of choice. This module is about how backbone.js transfers model data to and from the server. Same origin policy is a browser security feature that greatly complicates data synchronization. Cross-origin resource sharing also known as CORS is a technology that makes it easy again. There are two collection operations that sync to the server-- create and fetch. Models have three operations that sync to the server-- fetch, save, and destroy. The server has to implement a simple RESTful HTTP API. Backbone.sync is the function responsible for all server communications overrided at the model collection of global level to change the way that model synchronization works. Backbone.localStorage is a simple plug-in that overrides the global Backbone.sync function converting the default service synchronization into local synchronization that saves all data to the browser's local storage.
How Backbone.js uses the Server
Backbone uses RESTful HTTP web request to synchronize data to and from a server. Model data is serialized as JSON for network transmission. There is no rule that the server used by Backbone for persistence must be the same server that originally served the application, although the same origin policy does apply. The same origin policy prevents scripts from accessing resources belonging to another site. Among other things, this means that a backbone application cannot synchronize models to a server other than the server that served the current page. For the purposes of the same origin policy, an origin is defined as the application protocol, usually HTTP or HTTPS, combined with the domain name and port number. This is an example of an origin. Because these two URLs have the same protocol, domain, and port number, the same origin policy considers them the same origin. These two URLs have different port numbers and, therefore, different origins. And these two URLs have different application layer protocols and are, therefore, different origins. Cross-origin resource sharing, usually referred to as CORS, is a technology that allows cross origin request. It is an explicit exception to the same origin policy. To implement cross-origin resource sharing a server must include a set of special headers unit types HTTP responses. CORS is an alternative to JSONP. JSONP is supported in all the browsers while CORS is not. CORS supports all HTTP verbs JSONP is limited to get. All the browsers don't support CORS. Let's have a look at this port matrix. So we can see that CORS is not supported in versions of Internet Explorer less than 10, not supported in Firefox 3. It's supported in all the versions of Chrome and most other browsers.
The Server
Backbone's persistence protocol is very similar to other RESTful systems. It maps HTTP verbs to data operations. Data fetching is accomplished by the GET verb. Data creation uses an HTTP POST, updates are done via HTTP PUT, and delete use the HTTP DELETE verb. The only slight specialization is that when a new resource is created, Backbone expects the server to return an ID property that uniquely identifies the new resource on the server. The server can be any RESTful HTTP server technology. Ruby on Rails is a popular choice, but ASAP.Net, Web API, Express, Jingo (phonetic), and all the rest are also perfectly capable. I'm going to throw in a very quick demonstration now that will show a basic model synchronization to a server. Let me say the first thing we need to do is create a model and I'm going to define a new model type called Book, so that I can give it a URL that represents its location on the server. And this URL is the location where it happened to know that there is a Backbone compatible server up and running. Now, I can create a new model from that type. ( Pause ) Let's give it some data. ( Pause ) If I call the models save method, that's going to cause the model to be persisted through the server. That's an asynchronous operation, but I can provide success and error functions to the save method, and one of these will get called when the save operation is completed. Obviously, which one gets called depends on whether the save operation was a success or not. Success is defined as resulting in a HTTP 200 status code. If the server returns any status code other than 200, then backbone considers that an error. In my success and error callbacks I'm going to do a display an alert indicating what the result was, either done or error. And that's all that's required to get a client side backbone application syncing to a server. When I run this example, it alerted done, meaning that the save operation was a success. Now, when we look in the network traffic, we see that when I called the save method, backbone did HTTP POST to the model's URL. And the content of that POST was the serialized version of my model. The server returned a response with the 200 OK status code and that's why I got the success callback. You notice also this access control allow headers in their response. These are the calls headers and that's what allows me to synchronize my model to-- without the loop.com even though my application is currently running jsfiddle.net. So, normally, because those are different domains, there would be an error. This is the body of the response from the server and it simply includes the new ID of my saved model. That was nice and easy. But what would happen if I tried to run the same script in Internet Explorer? This time I got the error callback which indicates to me that most likely this server returned an error status code. The reason that this doesn't work in Internet Explorer is because this version of Internet Explorer, Internet Explorer 9, does not support the CORS technology. So, cross-origin request are not allowed. Therefore, running my app on the jsfiddle.net domain, I'm not allowed to make a request to-- without the loop.com.
backbone-server
I searched for simple backbone server to use for demonstration and development purposes and couldn't find one. So I created a little app called backbone server. It implements all of the backbone synchronization operations. Backbone server is for teaching and testing purposes only. It can be used to synchronize any backbone models without configuration, but it stores the data on memory. So, when backbone server restarts all of these saved data is lost. The alternative to using backbone server and what a real application would have to move to is a specialized API created with ASP.Net MVC with API, ruby on rails, node.js or any of those certified web frameworks. ( Pause ) This is the Backbone server homepage on github. I can get a copy of backbone server by copying its git URL. Open at a console and clone the project. ( Pause ) So git has cloned Backbone server into a directory called backbone server. Before I can start this server, I have to install its dependencies which I can do using the command NPM install. This obviously requires no JS to be installed and NPM to be set up and working properly. ( Pause ) So it's going through and installing all of the dependencies for backbone server and all of the dependencies' dependencies. That's finish now so I can start backbone server by calling node and passing the script file which is rasterver.js. That's it. Backbone server is now running. And by default it runs on port 3002. In my browser if I got to localhost 3002, that's going to connect to backbone server and that just tells me that it's running. Here I've got a new console window. And I can use curl to make a request to backbone server, localhost 3002. Our request, some URL endpoint. This shows the details of the request that I've just made to backbone server and the response return from the server was 404 Not Found, and that's because I requested a URL that doesn't correspond to any saved backbone data. And if I go back to the console where the backbone server is running, there's a message that just saying that there was a request to read the people resource.
Collection Requests
Collections have a create method that you can use to instantiate a new model, add it to the collection, and save it to the server. Because this is an insert operation, backbone.js uses an HTTP POST. The new model's attributes are posted to the collection URL. If the server side save operation is successful, the server is expected to return a response with status code 200 which is the HTTP success code, and a body containing adjacent object that has an ID property which uniquely identifies the new model. We saw before that I can make a request to my local instance of backbone server with any made up URL, I think I used people before, and get a response. In this case, it's telling me that it does not have any resources at that URL. The Collection Create Method issues a POST to the backbone server telling it effectively to insert a new model. We can simulate backbone's part in that operation using curl. Here I'm using the option for DOVOS (phonetic) and X. X lets me specify that I want to use the POST HTTP verb and the URL that I'm going to post to is localhost:3002/people. I have to add a header to tell the server the content type, which is application JSON. And then the actual data in the body of the request, this is going to be some JSON. So that's the equivalent of what backbone does when you use the collection.create method or try to save a model for the first time. Curl has issued my request and here is a response from the server. I get a 200 OK so it was a successful request. Again, we see the CORS headers, the content type where the response is also application JSON, and the server has simply returned ID 0. So that's the ID of my newly created server side model. Now, if I make a GET request to that URL one more time, now, that I've saved the model to the server, I get that result back. So the server has returned all of the resources at that URL. At the moment there's just one and it's my model for Bob with ID 0. If I don't want to request the entire collection, I can also append the ID to the end of the URL. This URL represents a single resource instead of the full collection. And so this time it's returned just an object instead of an array. If you look at the backbone server terminal, you can see that it's providing some output of the various requests that are coming in, so very basic logging. Here I have a basic HTML template file. It's got JQuery included, underscore and backbone and a script block. Within the script block it defines a new model type called person and a new collection type called people. I can create an instance of that people collection type and then use the collections create method to simultaneously create a new model, add it to the collection, and save it to the server. And I think this time I'll use my local instance of backbone server of course Okay, now, when I load that page in Chrome, it's going to execute that Create Operation. And if I look in the network traffic in Chrome development tools, see there's a HTTP POST to local host 3002/people. The request payload includes the new model that I asked it to create. The response is 200 OK and it's responded with the ID of the new model. Jumping back to my console, again, I'm going to request the people collection. It returned in array containing three models including the ones that I just saved from Chrome. Collections have one more synchronization method Fetch. Fetch triggers a GET request to the collections URL. The server is expected to return a response with a body containing adjacent string that represents the entire collection. The response data is in array of all the models in the collection. ( Pause ) We've seen how the Collection Create Method works. I can also use the Collection Fetch Method. That's kind of populate my client side collection with all the model data from the server. If I reload my page, see this time, instead of the POST that was triggered by Create, I get a HTTP GET. ( Pause ) The request is to the same URL, but it's a GET instead of a POST, and the response is in array containing all the model objects that the server knows about. And this, of course, is the same result that I get if I use curl to do a GET to that URL.
Model Requests
Models have a fetch method similar to the fetch method on collections. The models fetch method requires the model to have an ID defined so that backbone can tell which model to retrieve. It retrieves just a single model, not the entire collection. ( Pause ) Continuing with my webpage sample, for this particular demonstration I need an instance of the person type. And the person has to have an ID and the ID has to be the ID of a model that exists on the server. I know that I have a person with ID 0. And now by calling person.fetch that's going to cause a GET request to the server. It's going to populate that model with the service side data for the person with ID 0. If I want to have access to that data when the request returns, I can provide a success callback which I could use to log the result. Now, jump back into Chrome. I reload the page once again. This time I get an error. But let that run through. It's saying that a URL property or function must be specified. I go back to the code. Here's my definition of a person type. Here is where I create the instance of the person and then call fetch. No way in any of that do I instruct backbone on what the service side location is for that model. So I have a couple of options. One is I can provide a URL property for the person type. Possibly the easiest thing I can do and arguably more common is add my new person model to the people collection. And once the person model is in the collection, the person model is able to derive its URL based on the collections URL. Now, when I run it again, it succeeds and my success callback logs the result from the server. If I look in the network traffic, we see a GET. This time the GET is to a slightly different URL. It's the collections URL with the model's ID appended to the end. It's 200 OK so success on the server side and the data that's returned is the JSON serialization of that model objects that are requested. Models have a save method that persist the current state of the model to the server. If the model has never been saved before, then backbone will issue an HTTP POST to create a new model. If the model has been saved before, then backbone will issue an HTTP PUT to update the model. When backbone creates a new model on the server, it uses an HTTP POST. The POST goes to the collections URL. As always, it needs the JSON content type and some JSON data. JSON data is going to be the JSON representation of the person model. So I'll create a new person called Robert. He's 24. So curl has a done a POST to the /people URL. The server has returned a HTTP 200 OK, and the identifier of the new model object. If we request the full content of the people collection now, there's the new model that I just created. Now, if Robert has a birthday and we want to update that model in the server, it's very similar to creating a new model except that we change the HTTP web to PUT and change the URL to the models URL instead of the collections URL. The Robert model has an ID of 3, so his URL is the collection URL which 3 appended to the end. And I'm just going to change some data. We'll make Robert's age slightly different. Consider request was a PUT to a model's URL, the model with ID of 3. The response 200 OK and there's no content in the body of an update. I'll request the collection again. The Robert model has been updated. I actually made a mistake there and forgot to include the ID in the update, so Backbone server has deleted the ID property from that model. Fortunately, I can put it back. ( Pause ) Saving a new model with backbone is as simple as creating a new model. ( Pause ) And calling the save method. Once again I have to add the model to the collection so the Backbone can work at its URL and reload the page in Chrome. Backbone has made a POST request to the collection URL. The content of that request is the data of my new model. The response is the ID. Now, that I have a saved model, I'm going to fetch that model from the server. And in the success callback, I'll make it changed to the model. It was 22 before so now it can be 23, and then save that change. Reload in Chrome. Now, because I'm updating an existing saved model, backbone is clever and it knows to-- PUT to the models URL. It sends the full serialized representation of the model including the property that I changed. And the response is simply OK. The Destroy Method deletes a single model from the server and removes the model from its client-side collection. Backbone uses the HTTP DELETE verb for the request to the server. The request issued for a delete uses the DELETE verb and its URL is the URL of a model. So the request here is going to delete the person model with the ID of 1. Here you can see the request. Use the DELETE verb. The URL was the URL of model with the ID of 1. Response is just a plain HTTP 200 OK. You find out query the entire collection. You can see that the model with ID 1 has been destroyed. My backbone example remains much the same. Create a collection, create a person model for person that already exist, so provide an ID that exists on the server. Add the person model to the collection. This time instead of fetching, I want to destroy. Reload that up in Chrome. Here is the request the Backbone issued, uses the DELETE verb. The URL is the URL of the model to delete. And the response is OK.
Backbone.sync
Backbone.sync is the function that implements backbone's default synchronization behavior. By replacing Backbone.sync you can completely or partially redefine backbone's model persistence. A good example is the Backbone.localStorage plugin, which replaces backbone's default RESTful HTTP persistence with persistence to the browser's local storage. Backbone.sync understands four operations, Create, Read, Update, and Delete If you override Backbone.sync, you must support each of these operations. Backbone.sync can be replaced for a model, a collection, or globally. Backbone.localStorage is a plugin that allows Backbone.js to fetch, save, and destroy without a server. Instead of syncing to a server, Backbone.localStorage syncs to the browser's local storage. This is the same webpage skeleton that I've used in the past including backbone's required dependencies. And I've also included one extra script which is the Backbone.localStorage plugin. To use the local storage plugin to replace Backbone's syncing behavior requires two steps. The first is including the script. The second is to configure our local storage when defining a collection. Here I'm defining a new collection type called People and one of the properties that I post to the extend function is called Local Storage and it's a new instance of this local storage. The rest of the script block instantiates a people collection with a couple of model objects, saves those model objects, and then fetches the model objects. If I open that page in Chrome and go to the network tab and XHR. For normal backbone application I would say several network request here to save the various models and then to fetch them from the server. Because I'm using backbone local storage, there's no network operations involved. The model is being saved in the browser's local storage. If we have a look under the Resources tab and local storage, we can see that it's created these three records, one for each of the models that are saved and one representing the collection. ( Pause )
Summary
Backbone.js synchronizes with the server via a simple RESTful protocol. Backbone.sync is the single function that implements Backbone synchronization behavior. It can be partially or completely replaced to implement customs synchronization. Backbone.sync can be customized for a model, a collection, or globally. The same origin policy prevents a Backbone.js application from making request to different origins. You would get errors if your models and collections sync to an origin other than the one that serve the current page. For some modern browsers, you can get around this by using cross-origin resource sharing a.k.a. CORS. Backbone.js default synching behavior maps, create read update, and delete operations to a synchronized RESTful server request. It's easy to implement a custom server, or for testing and discovery purposes you can use Backbone server.
Testing
Overview
Liam McLennan: Love it or hate it, testing is a useful tool to help you build backbone JS applications that work correctly. This module will cover the basics of client-side JavaScript testing, as well as some backbone-specific tips for how to test models, views, and routers. There are plenty of good reasons to test any software that you write. But it's extra important, when working with client-side JavaScript. A good test suite provides a safety net that protects you from runtime errors, enables change, and facilitates cross-browser development. There are hundreds of testing tools available for JavaScript. It doesn't matter what you use. To begin with, the simpler, the better. For this module, I will use the Jasmine testing tool for all of the examples. Testing models is a breeze, if they are implemented properly. I'll demonstrate a simple scenario and how to test the models. Building on the model-testing example, I'll demonstrate how views can be tested. Most JavaScript testing tools require you to open a browser and load a web page to run your tests. That's a lot of friction, and it can become annoying or discourage you from running tests as often as you should. We will finish the module by exploring some tools and techniques for running JavaScript tests from the command line. There are three primary purposes for testing backbone JS applications. The first is to catch bugs. JavaScript is a dynamic language, and using a dynamic language comes with a trade-off. In exchange for power and flexibility, you lose the compile-time safety net that statically-typed languages provide. To counter this increased risk, it's necessary to rely upon tests, to verify that the code is correct. The second motivation for testing is to enable change. Once again, due to JavaScript's dynamic typing, change can be extremely difficult in a large application. Tests are required, if we are to be able to make changes and still have confidence that the application is working properly. Finally, every browser is different, and getting a client-side application to work properly across a set of browsers is difficult and requires constant vigilance. Having a suite of automated tests and a way to run them in different browsers is incredibly helpful.
Testing Tools
Test runners are simple tools that have two -- sometimes three -- responsibilities. Firstly, they collect and execute tests. They also take care of collecting and reporting the results. Optionally, they often include an assertion library for checking out assumptions of what a program will do and generating failures, when our expectations are not met. There are many test runners available, and which one you choose is largely a personal and stylistic decision. Some of the common ones that you will notice people using are Jasmine, Mocha, and QUnit. This is a Jasmine test. Jasmine tests are organized as specifications grouped into contexts. In this example, the context is 'some context'. And the specification is 'should show some observable behavior'. Each context and specification is defined by a string description and a function implementation. Contexts are often nested inside of other contexts. This is the Jasmine home page. It is a nicely-organized set of documentation that shows the features of Jasmine side-by-side with current examples. If you'd like to get started with Jasmine, reading the documentation is an excellent way to get up and running. This is another example of a Jasmine test skeleton. At the top level, it defines a context, labeled 'some context'. Within that top-level context, it defines another nested context. And within the nested context, there are two specifications. It 'should show some observable behavior', and it 'should show some other behavior'.
Testing Models
Models are easy to test, because they have minimal dependencies. The high-level pattern for testing models is: Step 1, initialize a model with a specific state. Step 2, test that the model's behavior matches expectations. And Step 3, repeat until all of the model's behavior has been verified. Consider the testing of a model that represents a rectangle. A rectangle model has two attributes -- length and width. We can specify the behavior of a rectangle model like this. The first line describes the top-level context for the specification, the rectangle model. The second line describes the specific context: A rectangle with length 7 and width 4. The third and fourth lines describe specific observations that can be made about the rectangle model, in the given context, that the area should be 28 and the perimeter should be 22. Remember the test pattern for backbone models. Step 1, initialize the model with a specific state that maps to the test context. Step 2, test that the model's behavior matches expectations that maps to the test's specifications. Let's take our rectangle specification from the previous slide and convert it into a Jasmine specification. The first line becomes a Jasmine context, declared with a call to the describe function. This is the top-level context for the rectangle model. It will contain all other contexts. The next step is certifying a more specific context, with length 7 and width 4. This maps to another Jasmine context nested inside the rectangle context. According to our original specification, a rectangle with length 7 and width 4 should have an area of 28. Jasmine expresses this as a specification defined by calling the it function. Like the describe function, the it function expects two parameters: A string describing the spec; and a function implementing it. The implementation of the specification belongs in the function body. We have one more specification to include. The rectangle with length 7 and width 4 should have a perimeter of 22. Within the length 7 and width 4 context, we add another specification, by making another call to the it function. Now we've seen how Jasmine works, let's implement our rectangle tests. Jasmine tests run in the browser. So our starting point is to create a HTML web page that will function as our Jasmine Test Runner. The example that I have here is almost entirely taken from the Jasmine library examples. It's a single web page. It includes a style sheet, for formatting the test output; some Jasmine JavaScript files; a copy of jQuery, underscore, and backbone. We have to include the scripts that we will be testing. And we have to include the scripts that contain our tests themselves. And finally, the page has a script book, which is used to initialize Jasmine and run the tests. This is the file that's going to contain our rectangle tests. As I showed in the PowerPoint slides, the first step is to create a context for the rectangle. This is the top-level context for testing the rectangle model. All other contexts will be defined inside of this one. Our scenario has used a nested context, with length 7 and width of 4. And within that context, we had two specifications. The first one, it should have an area of 28. And the second one, it should have a perimeter of 22. There's one more important file in this scenario, and that is the rectangle.js. This is a script file that will hold the definition of the rectangle. At the moment, it's empty. So we haven't started defining our rectangle model yet. But, now that we have the spec runner hooked up and the beginnings of a rectangle specification, that's enough to load this up in the browser and get Jasmine to run the tests. This is what the Test Runner looks like at this stage. It has some basic information, telling us which version of Jasmine is being run. There's timing statistics for the full suite of tests. A summary of the test run. So there's -- telling me that we have two specifications and they're both currently passing. And this shows the structure of that test: The two contexts, and then the two specs nested inside. Let's add some implementations to this specification. At the very top level, we need to implement this context. And the context is that we have some kind of a rectangle. So I'll create a variable to hold our rectangle type. Within the nested context, again, I need to implement that context. And the context is that I have a rectangle with length of 7 and a width of 4. At the moment, there's a variable called rectangle, but nothing has been assigned to it. Jasmine has another function called beforeEach, and this is what I can use to set up the function for my context. The argument to beforeEach is a function. And within that function is where I get to define what 'with length 7 and width 4' context means. Since Rectangle is going to be a backbone model, to give it a length of 7 and a width of 4, I use the set function. And I know from looking at this that there will be a runtime error. I can't set attributes on a model that hasn't been instantiated yet. So I go back to my rectangle context and add a beforeEach to that, as well. And within this beforeEach, all I'm going to do is instantiate my Rectangle model. Also, I haven't defined the Rectangle model yet, so this code is going to cause a runtime error, as well. Just to prove that, I try to run it now, I get two failures. And the reason, the area given, this app is not defined. So when the Jasmine Test Runner tries to set up the rectangle context and tries to instantiate a new app.Rectangle, this value app doesn't exist, so that's throwing an exception. So now it's time to get into our actual rectangle, so this is the object that we're testing. First thing I'm going to do is define a top-level app object. And all of the types and objects within my application will hang off this app namespace. This is an immediately-invoked function expression. I've defined a function and then wrapped that function in parentheses. And as soon as that function is defined, I then immediately invoke it, and passing my app object as the argument. With my immediately-invoked function, the app object is aliased to shapes. So shapes and app refer to the same object. And the reason that we often use immediately-invoked function expressions in situations like this, is that I'm now free to define whatever functions and variables I want within this immediately-invoked function expression. And those functions and variables will be scoped to that function. So there's no leakage of names into the global context. I jump back to the specification for a second. The rectangle context tries to create a new app.Rectangle. So I need to create this rectangle model type. I do that by using the now-familiar Backbone.Model.Extend. That should be enough to get my test passing again. Both tests are now passing. But they still don't test anything, really. My first specification is it should have an area of 28. So now I'll add an implementation for that specification. The Jasmine test assertions usually start with a call to expect. And within that call, we'll provide find a value. For this test, I'm going to say rectangle.area. And then we chain some assertion on the end of that. So I'm going to say I expect rectangle.area to be 28. Jasmine uses this kind of syntax, because it makes it possible to read the test assertions and have a reasonable idea what they do. So we read this as expect rectangle.area to be 28. Run the tests. The tests fail. Object has no method area. That's because my rectangle type doesn't define an area method. So let's add one. Area is a function. No arguments. And it's going to return the rectangle's length multiplied by the rectangle's width. Now, the tests pass again. The next specification for the rectangle is it should have a perimeter of 22. The implementation is similar to the last one. Expect rectangle.perimeter to be 22. The test fails. Need to go back to the rectangle implementation and add a perimeter method. And the perimeter will be two times the rectangle's length plus two times the rectangle's width. And the tests pass again. At this point, I've completed the specification from the PowerPoint slides, but I haven't fully described the behavior of a rectangle. So, what I can do is add another context. We know that our rectangle functions correctly, given a length of 7 and a width of 4. But I wonder what our rectangle does, if it has length and width that are equal. In other words, if your rectangle's actually a square. Now we have a second context, and we need to implement that context. From my application's point of view, what does it mean for a rectangle to have equal length and width? Well, one example would be a rectangle with length of 5 and width of 5. And my specification for this scenario is it should be a square. How will my test know if it's a square? Well, it'll call the isSquare method. That test fails, because the isSquare method has not been defined. Back to the rectangle code. Add another method, isSquare. And I can implement this method by testing if the rectangle's length and width are the same, if the length is equal to the width. And that makes my test pass. Now we see what the Test Runner looks like with more than one nested specification. Within my rectangle context, I have two contexts defined, each with their own specification. Now I know that if my rectangle happens to be a square, then the isSquare method will return true. That's great, but it raises the question of what will the isSquare method return, if my rectangle is not a square? Here I have a choice. I could add another specification to my previous rectangle context. Or I could define a new context, specifically for the purpose of defining how a rectangle behaves, when the rectangle is not a square. And that's what I'll choose to do. So, my new context is, with unequal length and width. Once again, I have to implement that context. And I'm going to do that by setting the length and width of the rectangle. In this case, I'll say length of 5, width of 3. And my specification will be 'it should not be a square'. And the way that I express that in code is to say expect rectangle.isSquare to be false. I shouldn't have to change my rectangle implementation. That test just passes. Now I want to consider the scenario where someone tries to create a rectangle or modify a rectangle with length and width values that don't make any sense. So I'll create a new context called 'describe setting invalid values'. What kind of invalid values can we have for a rectangle? Well, I'll make a new context -- a nested context -- for 'describe negative.length or width'. I don't want someone to be able to create a rectangle with a negative length or a negative width. My first specification can be it should throw an error, if the length or width is set to a negative value. This time, we have to do something a little bit differently. Normally, the pattern that we follow is that for a particular context, we use the beforeEach method to set up the context. But in this case, we can't set a negative length or width in the context, because we want that to be an error. We want that to throw an exception as soon as it happens. So I'm going to set the invalid values within the actual specification itself, so that I can trap the error. And the way I'm going to do that is by defining a function, setDimensions. And this is going to set the invalid values on my rectangle. So, go with a length of 1, which is fine, but a width of minus 9. And then I'll expect setDimensions to throw. There's also a slightly new Jasmine syntax. And what's going to happen here is when we get to this assertion, Jasmine will run the setDimensions function. And my assertion is that it should throw an exception. Jump to the Test Runner and run that. I have a failing test. And the reason is that setDimensions was expected to throw an exception, and it didn't. Go to the Rectangle code now. I need to implement a feature in my Rectangle type that throws an exception, if the consumer tries to set the length or the width to a negative value. The way I'm going to do that is by using an initialize method. And within the initialize method, I'm going to add an event handler for the change of event. When any attribute on my rectangle is changed, I want to test that the length attribute, if it's less than or equal to zero, or if the width attribute is less than or equal to zero, then that would be an invalid situation. So I'm going to throw a new error, Invalid dimensions. Because I'm using this within my event handler, I need to pass a context as a second argument to the on method. So I'll pass this in. Now try the test again. And it's passing. Terrific. Now that I think about it, a negative length or width is not the only possible invalid values. What about a zero length or width? Within my system, I don't want it to be possible for anyone to define a rectangle with a length or width equal to zero. And I'll express that as a specification. It should throw an error, if the length or width is set to zero. The implementation of this specification is very similar to the last one, except that instead of setting the width to minus 9, I'll set the width to a positive number and the length to zero. Looking at my rectangle implementation, when any attribute is changed, if the length is less than or equal to zero or the width is less than or equal to zero, it's going to throw an error. So I think I've already got this specification covered. Run the test to make sure. And that passes. And that's it for my backbone JS model specification testing.
Testing Views
Write testable views by following some simple rules. Don't depend on any specific elements in the DOM. Either render a completely new DOM element for the view or render into an element passed to the view's constructor. A view can be considered to earn its DOM element and everything nested within that DOM element, but it should never access DOM elements outside of its element. Some of the things that we can test for views, besides testing the methods on the view, are the elements rendered by the view and the events triggered by the view. To complement our earlier rectangle model specification, let's create and test a rectangle view. The top-level context is the Rectangle View. The nested context is that the view model has a length of 70 and a width of 40. The specifications are that the Rectangle View should render as a div with the class rectangle, and that it should have dimensions of 70 by 40, and that it should raise the event rectangle:selected when clicked. The first step to add this new specification is to modify our Jasmine Test Runner HTML file. Part of its job is to include the scripts being tested, as well as the specification scripts. So, if we want to add a Rectangle View and corresponding specifications, we have to import their script files. Add a new script tag to import a script called RectangleView. And I'll add another script tag to import a Rectangle View spec. This is my Rectangle View spec script. This file will contain all of my tests for the rectangle backbone view. Here are the specifications that we defined for the Rectangle View. My task now is to translate these specifications into Jasmine tests. As before, the starting point is to create a context for the Rectangle View. And within that outer context, a second context, this time for describing the rectangle with length 70 and width 40. For the Rectangle View context, I'll need a Rectangle View variable. And within my nested context, I'll use the beforeEach method to initialize that context. The first thing I need is a rectangle model with the appropriate dimensions. And then I can initialize the Rectangle View. When I create the Rectangle View, I'll set its model property to be my rectangle model. And the very last thing I need to do to establish this context is to tell the Rectangle View that it's time to render. That's enough to establish the context for this test. Now I can add my first specification. It should render a div with class rectangle. There's really two parts to this specification. So, at this point, I think I'll decide to split it into two specifications. It should render a div and it should render with class rectangle. These are independent expectations that I can make separately. And it's important to split them out, because they can also fail separately. So in the circumstance where one of the specifications passes and the other fails, I want to be able to see that separation. To test that the view renders as a div, I'll use the familiar expect method, access my view and its element property. And the element property has a tag name. I expect that to be div. At this point, I have a test that I can run. When I run the specifications, I get two errors. The first says, cannot read property rectangle view of undefined. And the error's coming from rectangle view spec, line 16. Line 16 of my rectangle view spec is where I instantiate a new instance of the rectangle view. The reason that that's throwing an exception is that I haven't yet defined the rectangle view. So I'll do that now. Again, I want to use a top-level app namespace. I'll define it like this, so that if app already exists, then we'll use that one. If this is the first time that I've needed an app namespace, then we'll just create a new empty object for it. As with the model example, I'm going to define the rectangle view inside an immediately-invoked function expression. And I'll attach my new view type to a .views sub-namespace. And the type is going to be called RectangleView, which extends from Backbone.View. That at least gets our test passing. In the case of our second specification, it should render with class rectangle. There is still no implementation. How would you implement this specification? Well, I'm going to use the $el property on rectangleView and the hasClass method from jQuery to look for the rectangle class. hasClass returns a boolean. So I'm going to expect that hasClass returns true. When I run the test, it fails. Expected false to be true. I go to the implementation of the rectangle view and add a class name, rectangle. Now the test passes. So these two specifications are complete. That was our first specification. The next one is 'should have dimensions 70 by 40'. I use the it method to add another specification. Within this specification, I need to check the dimensions of the rendered view. And I can do that, using the width and height methods on the jQuery element. Expect the width to be 70. And I expect the height to be 40. And that causes my tests to fail. I want to control the rendered output for my rectangle view. So at this point, I'd like to add a render method. And within the render method, I'll set the width and height. The width is set to the model's width attribute. And the height is set to the model's height attribute. And now that test passes, too. My final specification is that the view should raise the rectangle:selected event when clicked. To assert an expectation that an event was raised, I'll create a variable, rectangleSelectedRaised. That's going to represent whether or not that event was raised. Then I'll attach an event handler to an application event aggregator object. This doesn't exist yet, but in this test, I'll just write the test as though that object existed, and then I'll go create it. So, within the application event aggregator, I'm going to say that when the rectangle:selected event is raised, invoke this callback and change the value of the rectangleSelectedRaised variable to true. Finally, to trigger that behavior and access the element of the rectangle view and call the click method, we'll trigger a click event. And then the assertion, assert that rectangleSelectedRaised is true. As I mentioned, the app.eventAggregator property doesn't exist, so I know this test will fail. So before I even try to run it, I'm going to go back to my rectangle view. And after the definition of the view, I'll add an event aggregator. To create an event aggregator, I'll use underscore's extend function. And with the extend function, I'm going to mix in the Backbone.Events module into an empty object. And that becomes my event aggregator -- just a plain JavaScript object with the event module behaviors that I can use to trigger events and be notified of events being triggered. That test fails. Expected false to be true for my event specification. So the test fails, because it expects the rectangleSelected event to be triggered, when the rectangle's clicked, but that doesn't happen, because I haven't written any code to do that. In my rectangle view, I'll add an events property. Event I care about is the click event. So when a DOM click event occurs on the rectangle view's element, it will use the event aggregator and trigger the Rectangle:Selected event. And that's got the last of my specifications passing. A nice benefit of this style of defining tests is that the test output serves as a handy documentation of what the system does. As long as I keep these tests accurate and passing, I'll always have living documentation of what the system does and which parts of it are working.
Testing Routes
Routes and routers are closely tied to the browser environment. It's not easy to test them in isolation. Therefore, my preferred strategy is to keep routers as simple as possible. Defer all real work to some other independently-testable piece. Limit the responsibility of route handlers to defining which part of your application is invoked, when that route is triggered. If you really want to test backbone's implementation of routing, then you need to do a full functional test, through the user interface.
Testing Without a Browser
Running tests in a browser is inconvenient, slow, and not especially compatible with headless, continuous integration servers. It's also difficult to integrate with any existing automated tests that you may have in your project. For running tests that do not require a DOM, you can use Jasmine-node or Mocha. Jasmine-node is a project that makes it possible to execute Jasmine tests from the no-JS environment. Mocha is a purpose-built testing system for the no-JS environment. To add basic DOM capabilities to Jasmine-node or Mocha, you can use a project called zombie.js, which provides a simulated browser environment. Be aware that while zombie.js is very fast, it's not difficult to find incompatibilities between zombie.js and real browsers. The other option is to use a full browser, but in a way that can be automated from the command line. phantom.js is a headless, web kit browser with a JavaScript API. We can use phantom from the command line to open a browser, load a test page, execute our JavaScript test, extract the results, and exit. Because phantom.js is a full, headless browser environment, it's relatively slow, but also extremely accurate. Jasmine-node is a project that makes it possible to run Jasmine tests on node.js. You can run your tests from the command line without using a browser. The tests will only work if they do not depend on any of the browser's APIs, such as the DOM. This is the Jasmine-node home page. If we look at the readme, it tells us how to install Jasmine-node, which is by using the npm install command. And then once we have it installed, the command to run it is jasmine-node. And there's a whole series of different switches that you can use, to customize its behavior. So, I'm going to install Jasmine-node globally, using npm install. This depends upon having node.js and npm installed on my system. Jasmine-node is now installed. I have a directory called spec, which contains my rectangle specification, which is a set of Jasmine tests. From the command line, I can run Jasmine-node and tell it where my specs live. And that gives me the command line execution of my Jasmine tests. You'll notice that these tests, which pass in the browser, are all failing, when run from the command line. Now, if I go right back to the beginning, the very first error is reference error, app is not defined. When I was running my Jasmine specs in a browser, I was relying upon the browser to load those scripts and bring them together in the same environment. But now that I'm running tests from node.js, Jasmine-node is smart enough to find my spec files and load them and run them. But what goes wrong is that the actual implementation code is never loaded. So for my test to work with Jasmine-node, I have to do that manually. And the way to do that is by using node.js's require command. So within my spec file, I declare a variable app and set it to require, and then the path to my implementation, which is in rectangle.js. And now, when my specification tries to create a new instance of the app.rectangle, it will be able to find that code. I'll run the Jasmine-node command again. It fails, but with a different error. This time, the problem appears to occur on the first usage of backbone. In a reference error, backbone is not defined. It's ultimately the same problem as last time. When I run my Jasmine tests in the browser, I have script tags for all the different libraries that I need, and the browser is responsible for loading them all into the environment. But now that I'm running my tests on node, I have to be explicit in telling it how to get backbone. And the very first problem is it doesn't even have the scripts for backbone. Again, I can use npm to install backbone locally for my app. The command is npm install backbone. What that's done is created a subdirectory under the current path called node_modules. And within node_modules is a copy of backbone.js. Now that I've installed backbone, I need to reference and load it within my script. Backbone is used within my rectangle.js script. So before backbone is used, I need to define it and use the require function to load the backbone module. I'm still getting an error. The specification is having trouble loading the rectangle implementation. The reason is, although I correctly used require to load rectangle.js script, within rectangle.js, I haven't explicitly told node what are the public types to export. When you define a module in node.js, you can export types, using module.exports. And the simplest thing I can do for this example is to export the app variable. Within my spec, when I require rectangle.js, the result is going to be the entire app object. And now, that's enough to make my tests pass, executed on the command line. We get handy, colored output from Jasmine-node, indicating that there are six tests, and they've all passed successfully, in eight thousandths of a second. What if you want to be able to run tests from the console and be able to run tests that depend on the browser? phantom.js is a headless webkit with a JavaScript API. Think of it as a command line interface to a browser with no GUI. The browser part of the phantom.js is webkit. Webkit is the open source browser engine that powers Safari, Google Chrome, and the Android browser. phantom.js lets us run tests in a browser from the command line. This is the phantom.js home page. So the first thing we need to do is download phantom.js. phantom.js comes as a zip file or an archive. And when extracted, this is the contents. The file most important for our purposes is phantomjs.exe. This is the executable that we run on the command line to use phantom.js. phantom.js is an open source project, and this is it's github home page. Here you'll find documentation. There's also a useful examples directory. It shows a set of different things that you can achieve with phantom.js. For testing JavaScript, there's a set of scripts in the examples directory that begin with run -- run-jasmine and run-qunit. These are helper scripts that integrate phantom.js with testing frameworks, run-jasmine for Jasmine and run-qunit for QUnit. I'm going to show you how to run our existing Jasmine tests from the command line, using phantom.js. So, to do that, I've downloaded the run-jasmine script and made some modifications. I had to modify it, because the version that's in the phantom.js repo wasn't compatible with the version of Jasmine that I'm using. Here I have a console window that's open at the location of my Jasmine test project. If we look in spec forward slash RectangleSpec.js, this is the original specification we wrote for a rectangle. It has assertions for the area and perimeter of a rectangle, code to detect if a rectangle is actually a square, and error handling for invalid values. I can open the Jasmine tests in a browser. And here you can see that there are six specifications, all of which are passing, when the tests are run from the browser. Now what we're going to do is go back to the console and use phantom.js to load this web page in a headless browser, wait for the test to finish running, and extract the results. That's the phantom.js executable. The first argument is the run-jasmine adapter, and the second argument is the web page that runs that Jasmine specs. So phantom is now loading a headless browser, loading the spec runner to HTML web page, executing the tests, and then reporting the results. And this is the successful outcome. If I look at the dollar question-mark variable in BASH, that shows me the last exit code. And zero is the exit code that indicates success. Exit codes are important, because if I want to use phantom.js to get my JavaScript tests into a continuous integration build, most likely the continuous integration platform will use the process's exit code to determine whether or not the test passed. So in this case, an exit code of zero would indicate that all of my tests passed. To prove that this is actually working, I'll go back to my rectangle specification here and change this test to fail. A rectangle with length 7 and width 4 should have an area of 28, but I'm asserting that the area should be 38. So that will generate a test failure. In the browser, you can see that I have one test failing, expected 28 to be 38. Now I'll rerun my tests from the command line. And this time I got an output error message. More importantly, if I inspect the last exit code, the last exit code is now 1. And 1 is an exit code that indicates that there was an error. In this case, it was a failing test.
Summary
Testing client-side JavaScript is tricky, but doable. It feels like early days for the tools and techniques that make it easier. I expect a lot of progress to be made in the next few years. I believe that testing helps us work faster. Especially in the medium and long term, it's an investment with excellent return. When testing models, try to minimize their dependencies. Initialize the model to a known state and then assert that the model's behavior matches your expectations. When testing views, try to minimize their dependencies. Restrict views to their own DOM elements. Have the view create a new DOM element or attach to an existing element passed to the view's constructor. Keep routers so simple that you don't need to test them. Running JavaScript tests from the command line is often more efficient than running them in a browser. A number of tools exist to make this possible.
Course author
Liam McLennan
Liam is a software developer, technologist and product delivery expert. He thinks a lot about how to manage software delivery projects and how to grow high performance teams.
Course info
LevelIntermediate
Rating
(848)
My rating
Duration4h 42m
Released5 Sep 2012
Share course