The Practical Client

How To Build a Backbone Application with TypeScript, Part 1: Getting Started

Backbone is a popular library for creating MVC/MVVM-like applications in your client. Here's an introduction to Backbone and some best practices for creating a Single-Page Application with TypeScript.

For my last few columns, I've showed off how to use TypeScript by creating a single page application using several JavaScript libraries (including Knockout and Breeze). Now I'm going to change focus and show how to create a similar application, but this time using the popular Backbone library. Each upcoming column will look at implementing a typical "business application" piece of functionality (retrieving and displaying data, handling master/detail pages, performing create, read, update, and delete [CRUD] activities, handling errors and so on). Along the way, I'll also be looking at how to use TypeScript effectively. In addition to showing how to use Backbone, this column also demonstrates some best practices in setting up your project and defining the client-side equivalent of your middle-tier business objects/entities.

A Backbone Primer
In the client, a Backbone application consists of Models and Views, tied together through Collections and Events. A Model holds the data your page depends on, along with your client-side business logic. Models are gathered together into Collections either by adding Models directly to a Collection or adding functions-that-return-Models to a Collection.

A View provides a connection between a Model and your HTML page. A View is tied both to a Collection and to a section of your page's HTML. Views monitor the Events fired by Collections (or individual Models in the collection). If you make a change to a Model and fire an Event that a View is monitoring, the View will re-render the part of your HTML page to which it's attached. Views must know about Models they render but Models are unaware of the Views that render them, which facilitates sharing the Models among multiple applications (odds are that the View used by an application are unique to that application).

Backbone includes some other cool features: A RESTful JSON interface for talking to your server; History support to generate URLs for different states in an SPA; a Router that ties URLs to functions that create (or recreate) states in your page. I'll get to those, eventually.

On top of all this, virtually everything in Backbone can be overridden and replaced with a plug-in -- and there are lots of Backbone plug-ins. For example, your Views can generate the text for your page any way you want (including concatenating strings together), but it's a good idea to adopt some templating library that will simplify generating your text. Backbone has a dependency on Underscore, which includes templating as one of its features, but you can replace Underscore with the templating tool of your choice (many Backbone developers use Handlebars, for example). You don't need any of those plug-ins, but before you write any utility-like code, you should always check to see if there's a Backbone plug-in that will do the job for you. Over the course of building this application, I'll also look at integrating one or more plug-ins.

Configuring the Project
Recognizing that most of us aren't using Visual Studio 2013 yet, I've built this project in Visual Studio 2012. Mads Kristensen (who also created Web Essentials) has created an ASP.NET MVC project template that uses Backbone, but because that template was built with an earlier version of TypeScript, I decided not to use it (TypeScript is now at version 1.0). Instead, I use as my starting point the ASP.NET TypeScript project that's installed with the TypeScript plug-in. That choice means that I need to spend some time configuring my project.

I began by using NuGet Manager to add Backbone and the DefinitelyTyped definition files for jQuery, Backbone and Underscore to my project. Following up on my last column about managing dependencies among my TypeScript files, I also add the RequireJS library to handle downloading my code files from within my TypeScript code (by default, the Visual Studio 2012 TypeScript Project template configures your project to use RequireJS).

As the number of JavaScript libraries in my projects has increased, my Scripts folder -- when open -- has started taking more and more space in Solution Explorer. To combat that, under the Scripts folder, I now create a folder called Libraries and drag all of my JavaScript libraries into that folder. I also create another Scripts folder, called Application, to hold my own TypeScript code files. NuGet automatically adds definition files to a folder called Typings in the Scripts folder, so I leave those files where NuGet puts them.

Organizing the Code
I divide my code for this application into three files: SalesOrderApp.ts, which holds the code that initializes the page and kicks off the application; SalesOrderModels.ts, which holds my classes ("Models" in Backbone-speak); and SalesOrderViews.ts, which holds my Backbone Views. I use this division because I suspect my Views will probably only be used in this application but I can imagine using my Backbone models in other applications. Dividing my View and Model code into separate files supports that.

At the top of the SalesOrderApp.ts file, I set up shorthand namespaces for my two other files, and by using the require function, tell TypeScript to load those files when it loads SalesOrderApp:

import som = require("SalesOrderModels");
import sov = require( "SalesOrderViews" );

With those two shorthand namespaces defined, I can use them to define shorthand references to modules defined inside the files (you'll see an example of one of those modules -- CustomerModels -- near the end of this column):

import cms = som.CustomerModels;
import cvs = sov.CustomerViews;

I don't use the require function to load the various third-party libraries I'm depending on (for example, Backbone or jQuery). Instead, I use standard HTML script tags to have the browser download those files. That gives the browser a chance to cache those libraries at the client and skip re-downloading them for other pages on my site.

At the top of my SalesOrderModels files (which only needs Backbone), I add a reference to the Backbone definition file and a shorthand namespace to refer to its classes (the reference element/comment wouldn't be required in Visual Studio 2013):

/// <reference path="../typings/backbone/backbone.d.ts" />

import bb = Backbone;

My SalesOrderViews file needs references not only to Backbone and my SalesOrderModels file but also to Underscore, so I add references to those files at the top of the SalesOrderViews file. I don't bother setting up a shorthand reference to Underscore because its default reference (an underscore) can't be simplified. Because I've set up my SalesOrderModels to be loaded using the require function in SalesOrderApp, I must use the require function here also to add the shorthand reference to SalesOrderModels:

/// <reference path="../typings/underscore/underscore.d.ts" />
/// <reference path="../typings/backbone/backbone.d.ts" />

import bb = Backbone;
import som = require("SalesOrderModels");
import cms = som.CustomerModels;

Setting Up the Page
My application will start by providing a list of Customers in a dropdown list (users will select the customer whose sales order they want to view or update from this list). Listing 1 shows the initial version of the page with the Div tag that will hold my dropdown list and the script tags that load my third-party libraries. As you can see, one of the features of Backbone (as opposed to Knockout, for example) is that the page is pure HTML (no Backbone-specific tags).

Listing 1: A Simple Page for Backbone Processing
</head>
<body>
  <form>
  <h1>Sales Orders</h1>
    Select a Customer: <div id="Customers"></div>
  </form>
  <script src="Scripts/Libraries/jquery-1.7.2.min.js"></script>
  <script src="Scripts/Libraries/underscore.js"></script>
  <script src="Scripts/Libraries/backbone.min.js"></script>

In order to start my application processing, I use a script tag to load RequireJS, adding a data-main attribute that points to the file holding my application's startup code:

    <script data-main="Scripts/Application/SalesOrderApp.js" 
            type="text/javascript" 
            src="Scripts/Libraries/require.js"></script>

</body>
</html>

Defining the Objects
I'll wrap up this first column by looking at my Backbone Models (next month's column will look at rendering my Views). First, I declare a module to hold the Models that define customer data. To support using this module with the require function, I declare it with the export keyword:

export module CustomerModels
{

I begin defining my Customer Model with an interface:

export interface ICustomer
{
  Id: number;
  FirstName: string;
  LastName: string;
}

However, Backbone also expects my Models to include various members that Backbone requires (these members are defined in Backbone's Model class). In TypeScript, to support that, I define my Customer class as extending the Backbone Model class while also implementing my ICustomer interface:

export class Customer extends bb.Model implements ICustomer 
{

To make my models useful to Backbone, I need to store my Customer properties data in the underlying Backbone Model object. To update data in a Backbone Model, I must use functions on the Model class named get and set. The easiest way to integrate my Customer class with the Backbone Model is to define the properties in my ICustomer interface with getters and setters. Within those getters and setters, I can store my Customer data in the underlying Model object. As an example, here's what the implementation of my Customer's Id property looks like:

get Id(): number
{
  return this.get( 'Id' );
}
set Id(value:number)
{
  this.set( 'Id', value );
}

Finally, I want my Customer Model to have a constructor I can use to set all the properties on the class when I instantiate a Customer class. TypeScript requires a constructor in a class that extends another class begin by calling the super function. Other than that restriction, I can do whatever I want in the constructor. This constructor accepts a parameter for each property on the class and uses those parameters to set the properties:

constructor(Id: number, FirstName: string, LastName: string)
{
  super();
  this.Id = Id;
  this.FirstName = FirstName;
  this.LastName = LastName;
}

I can now create some dummy Customer data for testing purposes with code like this:

var cust: cms.CustomerModels.Customer;
cust = new cms.CustomerModels.Customer( 1, "Peter", "Vogel" );

With all the groundwork laid, I'm ready to get something up on the page with a Backbone View (or two). That's what I'll be doing next month.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube