Backbone.js
has quickly become one of JavaScript’s premier MV* frameworks. It offers simple flexibility, and language features that just make sense.

ASP.net MVC has also become one of the web’s most popular server side frameworks and the latest iteration, MVC4 was recently released into beta. One of it’s newer features is called, Web API and is a way of creating a REST service inside of an MVC application.

Along with the release of MVC4 is a new feature in Entity Framework Code First called Migrations. This offers a way of keeping track of all the changes to the data model over time.

And yet another popular web feature is CoffeeScript. Written by the same guy that created Backbone, and Underscore, CoffeeScript adds Ruby-esque language features to JavaScript, and also “transpiles” down into JavaScript. It can be compiled in a number of different ways including a coffee.exe, inside of Node.js, or directly in Visual Studio with Mindscape Web Workbench.

This post combines all of these exciting new things into one app for managing contacts.

First off, download and install the ASP.net MVC4 Beta.

Then go ahead and either get Mindscape, or get Node.js, and run

npm install -g coffee-script

in a command prompt or terminal window.

Next, open up Visual Studio and File –> New Project.

There should be a new option for ASP.NET MVC 4 Web Application. Hit ok, and then choose Empty from the next window.

This will give you a brand spankin’ new MVC4 application. Let’s begin with creating the data model.

Please note: All the code below is online at…

https://github.com/jcreamer898/BackboneCoffeeWebApi

The code below is broken down into Entity Framework, Web API, and CoffeeScript/Backbone.

Entity Framework

First off we’ll create a Customer class, and a DbContext.

    public class Customer
    {
        public int Id { get; set; }

        [Required]
        public string FirstName { get; set; }

        [Required]
        public string LastName { get; set; }

        [Required]
        [Email]
        public string Email { get; set; }

        public string Phone { get; set; }

        public string Description { get; set; }
    }
    public class ManagerContext : DbContext
    {
        public DbSet Customers { get; set; }
    }

The extra DataAnnotation of Email is from the NuGet package DataAnnotationsExtensions.MVC3.
Next is upgrading Entity Framework and Enabling migrations. This is accomplished by the following three commands in the Package Manager Console…

 

    >Update-Package EntityFramework
    >Enable-Migrations
    >Add-Migration FirstDatabaseCreate
    >Update-Database

A new folder called Migrations will be added to your solution and in it will be to files, Configuration, and TIMESTAMP_FirstDatabaseCreate.cs. The FirstDatabaseCreate.cs provides an up and down for moving through different versions of your database. Update-Database will go ahead and create the database for you.

The configuration file also has a seed method for seeding the database…

    internal sealed class Configuration : DbMigrationsConfiguration
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(CustomerManager.Models.ManagerContext context)
        {
            //  This method will be called after migrating to the latest version.

            //  You can use the DbSet.AddOrUpdate() helper extension method
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
            context.Customers.Add(new Customer
                                      {
                                          FirstName = "Joe",
                                          LastName = "Schmoe",
                                          Description = "Awesome",
                                          Email = "joe@schmoe.com",
                                          Phone = "6155551234"
                                      });
        }
    }

That’s it for the data model, now onto the Web API stuff. Web API is still currently in Beta, but it is in pretty darn good shape already as Scott Gu lists some of its awesome features. Most notable are Automatic Content Negotiation, built in IoC support, and a Modern HTTP model.

Web API

You’ll notice in a new MVC4 application, in the global.asax a new route will be added…

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

This allows you to begin adding Api controllers which inherit from a new class called ApiController. Go ahead and right click on Controllers and create a new controller. In the drop down box choose API Controller.

apicontroller

This will generate a new controller that looks something like…

    public class CustomersController : ApiController
    {
        // GET /api/customers
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET /api/customers/5
        public string Get(int id)
        {
            return "value";
        }

        // POST /api/customers
        public void Post(string value)
        {
        }

        // PUT /api/customers/5
        public void Put(int id, string value)
        {
        }

        // DELETE /api/customers/5
        public void Delete(int id)
        {
        }
    }

By convention the name of the Http method becomes the name of the Action in the Controller. Fast forward a little bit, adding in all of the retrieval methods for the the methods, and…

 

public class CustomersController : ApiController
    {
        private ManagerContext _managerContext;

        public CustomersController()
        {
            _managerContext = new ManagerContext();
        }

        // GET /api/customers
        public IEnumerable Get()
        {
            return _managerContext.Customers;
        }

        // GET /api/customers/5
        public Customer Get(int id)
        {
            return _managerContext.Customers.SingleOrDefault(c => c.Id == id);
        }

        // POST /api/customers
        public HttpResponseMessage Post(Customer customer)
        {
            JsonArray errors = ParseErrors();
            if (errors.Count > 0)
            {
                return new HttpResponseMessage(errors, HttpStatusCode.Forbidden);
            }
            _managerContext.Customers.Add(customer);
            _managerContext.SaveChanges();
            return new HttpResponseMessage(customer);
        }

        // PUT /api/customers/5
        public HttpResponseMessage Put(int id, Customer customer)
        {
            JsonArray errors = ParseErrors();
            if(errors.Count > 0)
            {
                return new HttpResponseMessage(errors, HttpStatusCode.Forbidden);
            }
            _managerContext.Entry(customer).State = EntityState.Modified;
            _managerContext.SaveChanges();
            return new HttpResponseMessage(customer);
        }

        // DELETE /api/customers/5
        public void Delete(int id)
        {
            var customer = _managerContext.Customers.SingleOrDefault(c => c.Id == id);
            _managerContext.Customers.Remove(customer);
            _managerContext.SaveChanges();
        }

        private JsonArray ParseErrors()
        {
            var errors = new JsonArray();
            // Validate movie
            if (!ModelState.IsValid)
            {

                foreach (var prop in ModelState.Values)
                {
                    if (prop.Errors.Any())
                    {
                        errors.Add(prop.Errors.First().ErrorMessage);
                    }
                }
            }
            return errors;
        }
    }

Whew, a lot going on there, but not too much. It’s pretty much routine in terms of retrieving things from a DbContext in EF. The newer-ish things are in the Put and Post you can see the return type is HttpResponseMessage. This class is used to wrap a response and return specific error codes. The other is a private method called ParseErrors. This method loops over all of the Values in the ModelState and determines if there are any errors. If so, it will gather them into an array and return them with a 403 error to the client.

That takes care of the Web API stuff for now. You can test that the API is working by firing up a debugger and hitting /Api/Customers. For further info on the Web API, checkout Jon Galloway’s series of 6 posts.

Next, create a HomeController, layout, and an index view just like in MVC3. Then go ahead and either add Backbone, Underscore, and Handlebars to the application. Handlebars.js is a very good templating framework for JavaScript.

CoffeeScript/Backbone

Enter CoffeeScript. Using the Mindscape Web Workbench extension, you can add CoffeeScript files to your application.

coffeescript

coffeescriptfile

Every time you write CoffeeScript and save it, it will automatically, “transpile” your CoffeeScript into JavaScript. Then you can reference app.js in the layout.

Now begin the Backbone CoffeeScript fun! I’ll start with the models.

class Customer extends Backbone.Model
	urlRoot: '/api/customers/'
	idAttribute: 'Id'
	validate: (attr) =>
		if !attr.FirstName
			return "First Name is required"
		if !attr.LastName
			return "Last Name is required"
		if !attr.Email
			return "Email Address is required"

class Customers extends Backbone.Collection
    url: '/api/customers/'
    model: Customer

This code creates a Customer Model as well as a Customer Collection. The Customer model has some validation that will come into play whenever the Customer’s attributes are set, or the model is saved. The urlRoot is the main Url from our Web API. Backbone.Sync will use GET on model.fetch POST on model.save when the Id of the model hasn’t been set, PUT on model.save when the Id IS set, and DELETE on model.destroy.

Next we’ll create some Customer Views.

class CustomerList extends Backbone.View
	initialize: =>
        @collection.on 'reset', @renderAll
        @collection.on 'add', @render
        @collection.fetch()
        _.bindAll @,'renderAll'
        return this
    renderAll: () =>
        @collection.each @render
        return this
    render: (model) =>
        item = new CustomerItem
            model: model
        @$el.append item.el
        return this

class CustomerItem extends Backbone.View
	tagName: 'tr'
	initialize: =>
        @template = Handlebars.compile($('#customer').html())
        @model.on('change', @render)
        @render()
        return this
    events:
        'click .remove': 'deleteItem',
        'click .edit': 'showEdit'
    render: =>
        html = @template @model.toJSON()
        @$el.html('').append(html)
        return this
	showEdit: (event) =>
		event.preventDefault()
		Vent.trigger 'edit', @model
		return this
	deleteItem: =>
		@model.destroy()
		@$.fadeOut 'fast'
		@remove()
		messages.success 'Deleted!'
		return this

class CustomerEdit extends Backbone.View
	el: '#customerEdit'
	events:
		'click #save': 'save'
		'click #cancel': 'cancel'
	initialize: =>
		Vent.on 'edit', @render
		@template = Handlebars.compile $('#customerEditTemplate').html()
	render: (model) =>
		if model
			@model = model
		else
			@model = new Customer()
		data = @model.toJSON()
		html = @template data
		@$el.html(html).show()
			.find('#first').focus()

		@model.on 'error', @showError
		return this;
	save: (event) =>
		@model.save
			'FirstName': @$el.find('#first').val()
			'LastName': @$el.find('#last').val()
			'Email': @$el.find('#email').val()
			'Phone': @$el.find('#phone').val()
			'Birthday': @$el.find('#birthday').val()
			'Description': @$el.find('#description').val()
		,
			wait: true
			success: =>
				messages.success 'Saved!'
				window.customers.add @model unless window.customers.any( (customer) =>
					return customer.get('Id') is @model.get('Id');
				)

				@$el.hide()
		return this
	cancel: =>
		@$el.hide()
	showError: (model, error) =>
		error = JSON.parse(error.responseText).join '<br   \ />' if (typeof error is 'object')
		messages.error error

The CustomerList is responsible for managing the entire list of customers. And anytime the collection is retrieved from the server or added to, it will create a new CustomerItem view and append it to the CustomerList. The CustomerItem uses a customer template found in the following HTML code, and is compiled with Handlebars.js.

The CustomerEdit view is used to edit, no surprise there! The initialize method stores a compiled Handlebars template for editing a customer. The save method grabs all of the values off of the inputs, and then calls model.save(). This of course causes the server to either PUT or POST depending on the result of model.isNew().

<script id="customer" type="text/html">
    <td>{{ FirstName }}</td>
    <td>{{ LastName }}</td>
    <td>{{ Email }}</td>
    <td colspan="2">
        <a href="#edit" class="edit" data-id="{{Id}}"><i class="icon-pencil"></i></a>
        <a href="#remove" class="remove" data-id="{{Id}}"><i class="icon-remove"></i></a>
    </td>
</script>

<script id="customerEditTemplate" type="text/html">
    <input type="hidden" name="id" id="id" value="{{Id}}"/>
    <input type="text" name="first" id="first" value="{{FirstName}}" placeholder="First Name"/>
    <input type="text" name="last" id="last" value="{{LastName}}" placeholder="Last Name"/>
    <input type="email" name="email" id="email" value="{{Email}}" placeholder="Email"/>
    <input type="text" name="phone" id="phone" value="{{Phone}}" placeholder="Phone"/>
    <textarea name="description" id="description">{{Description}}</textarea>
    <a class="btn btn-primary btn-large" id="save">Save</a>
    <a class="btn btn-primary btn-large" id="cancel">Cancel</a>
</script>

One of the interesting things in the CustomerItem view is the concept of a Vent. I saw Derick Bailey use this technique. It combines a few patterns, one called Pub/Sub or Observer, and another called the Mediator pattern.

The Observer pattern basically allows objects to publish or subscribe to events. Much the same way that when working with browsers, when a user clicks in the browser, the browser publishes a click event, and some JavaScript or jQuery code listens to the event.

The Mediator pattern assigns an object the responsibility of handling all pub/sub requests.

The Observer pattern is implemented through Backbone.Events, and can be used with the Mediator pattern by…

    class Vent extends Backbone.Events
    window.Vent = Vent

Now you have a global Vent object that is responsible for managing pub/sub events. In the CustomerItem view above you can see the line of code, Vent.trigger ‘edit’, @model

In CustomerEdit there is some code that listens to the edit event Vent.on ‘edit’, @render and fires the render method when it is triggered.

Also in CustomerEdit I used a view called MessageManager in order to display messages to the users.

class MessageManager extends Backbone.View
    el: '.alert'
    render: (type, message, opts) =>
        defaults =
            fade: 3000
        _.extend defaults, opts
        typeClass = "alert alert-#{type}";
        @$el.empty().prepend(message).removeClass().addClass(typeClass).fadeIn 'fast'
        setTimeout (=> @$el.fadeOut()), defaults.fade
    error: (message, opts) =>
        @render 'error', message, opts unless !message
    success: (message, opts) =>
        @render 'success', message, opts unless !message

Pretty basic stuff here, just a few methods for triggering different types of messages.

The last piece needed in the JavaScript code is the initialization of the different views.

$ ->
    window.customers = new Customers
    edit = new CustomerEdit()
    list = new CustomerList
        collection: customers
        el: '#customerList'

    $('#add').click ->
        Vent.trigger 'edit'
    $(".alert").alert()
    window.messages = new MessageManager()

This code here wraps the initialization in a jQuery ready, and then starts newing stuff up. An instance of the Customers collection, and an instance of the messages view are stored in the window so that they can be accessed anywhere in the app, and the edit and list views are also initialized.

Last is the simple attaching of a click handler to my add button which triggers the Edit event and allows for adding a new customer.

Again all of the above code is available at

https://github.com/jcreamer898/BackboneCoffeeWebApi/

And in the /Scripts/ directory there is an app.coffee, and it’s code behind app.js, as well as an actual representation of the JS at app.javascript.js

Hope you enjoy!

Tagged with:  

BackboneJS modular app using RequireJS

On December 9, 2011, in BackboneJS, JavaScript, RequireJS, by jonathancreamer

Write the App

Everyone knows that large scale JavaScript applications can get out of control really quickly really fast. Tools like jQuery are helpful in providing easy access to the DOM, but provide little to no help when piecing together a full fledge application. You’ll begin writing a few selectors here and there, manipulating some data, making some Ajax requests, and next thing you know your knee deep in a big bowl of Spaghetti code!

Bring on RequireJS.

RequireJS is a framework that adheres to the ComonJS specification for Asynchronous Modular Definitions (AMD). See this post by a fellow brewer, Derek Greer for some more info on getting started with Requirejs.

The basic idea behind RequireJS and AMD is to separate your code into Modules, and by Modules, I mean this definition found by Nicholas Zakas…

Module: An independent self-contained unit of a spacecraft.

Here, each module can exist separately, or together with the rest of the space ship.

This is a common way to solve JavaScript architecture as well.

To build a BackboneJS app with RequireJS…

  1. Download RequireJS (I used the Sample Require + jQuery App)
  2. Download the Order plugin
  3. Download UnderscoreJS (Backbone depends on Underscore)
  4. Get one of the AMD versions of Backbone from @jrburke off of one of his optamd branches. I used this one https://github.com/jrburke/backbone/blob/optamd/backbone.js
  5. Create a folder structure, and files like… (Bold means create a new empty file)
    /r.js (Copy from the RequireJS download)
    /app/index.htm
    /app/scripts/
    /app/scripts/require-jquery.js (Copy from the RequireJS download)
    /app/scripts/main.js
    /app/scripts/app.js
    /app/scripts/order.js
    /app/scripts/lib/underscore.js
    /app/scripts/lib/backbone.js
    /app/scripts/routers/home.js
    /app/scripts/views/welcome.js
    /app/scripts/models/movie.js
    /app/scripts/collections/movies.js
  6. Build models, views, routers, and collections at your leisure

First up is the index.htm file…

<!DOCTYPE html>
<html>
<head>
<title>BackboneJS Modular app with RequireJS</title>
<script data-main="scripts/main" src="scripts/require-jquery.js"></script>
</head>
<body>
    <h3>BackboneJS is awesome</h3>
    <div id="main"></div>
    <script id="moviesTemplate&qout; type="text/html">
        <div id="<%= model.name %>"></div>
    </script>
</body>
</html>

Then there’s main.js. This is where you’ll define the base of how your application will load…

require.config({
    'paths': {
        "underscore": "libs/underscore",
        "backbone": "libs/backbone"
    }
}); 

require([
	'order!libs/underscore',
	'order!libs/backbone',
	'order!app'
	],
	function(_,Backbone,app){
          app.init();
});

The require.config set’s up some paths for the modules that are defined with a name. Next, the require function takes in 2 arguments. First is an array of dependencies that main.js will load in. The order plugin makes sure that Underscore and Backbone get loaded in order as Require typically loads things in asynch which is bad for Backbone since it requires Underscore. Using the require-jquery verion includes jQuery 1.7 which now has AMD built in.  app references the app.js file you will create in a second. The function callback takes the loaded dependencies and passes them as arguments in the order you loaded them in, _, Backbone, and app.

Now for the app.js

define(['routers/home'], function(router){
    var init = function(){
        console.log("App Started...");
    };

    return { init: init};
});

Here we use define to create a new module. The first argument is an array of dependencies, and the second is a callback like the one on main.js which will fire once the dependencies have loaded. For now I am just requiring the home.js router.

The home.js router looks like…

define(['backbone','views/welcome'],function(Backbone,welcome){
    var homeRouter = Backbone.Router.extend({
        initialize: function(){
            Backbone.history.start();
        },
        routes: {
            '': 'home' // Default route
        },
        'home': function(){
            welcome.render();
        }
    });

    return new homeRouter();
});

Here we load in Backbone, and the welcome.js view…

define(['jquery','backbone','underscore', 'collections/movies'], function($, Backbone, _, movies){
    var welcomeView = Backbone.View.extend({
        el: "#main",
        initialize: function(){
            this.movies = new movies();
            this.template = _.template($("#moviesTemplate").html());
            this.movies.bind('add', this.addMovie, this);
        },
        render: function(){
            this.movies.add({ name: 'Empire Strikes Back'});
            this.movies.add({ name: 'Jurrasic Park'});
            this.movies.add({ name: 'The Last Crusade'});
        }
        addMovie: function(model){
            $(this.el).append(this.template({ model: model.toJSON() });
        }
    });

    return new welcomeView();
});

This one loads in jQuery, backbone, underscore, and our movies collection which looks like…

define(['backbone','underscore','models/movie'],function(Backbone,_,movie){
    var movies = Backbone.Collection.extend({
        model: movie,

    });

    return movies;
});

From here we get down to the model, movie…

define(['backbone','underscore'],function(Backbone,_){
    var movie = Backbone.Model.extend({});

    return movie;
});

So that’s how to create a basic app with a router, view, collection, and model.

Build with Node or Java

One of the cool parts about Require is it’s ability to build and compress your application for you. It requires an app.build.js file, r.js(which you download with the RequireJS) jQuery app, and if you want to compile with Java, you’ll need Rhino and Clojure (Thanks to @jrburke again for this help page on r.js) or if you want to use NodeJS you’ll just need Node for your OSOC (Operating System of Choice) . The app.build.js file will typically look something like…

({
    appDir: "../",
    baseUrl: "scripts/",
    dir: "../../app-build/",
    //Comment out the optimize line if you want
    //the code minified by UglifyJS
    //optimize: "none",

    paths: {
        "jquery": "require-jquery",
		"underscore": "libs/underscore",
		"backbone": "libs/backbone"
    },

    modules: [
        //Optimize the require-jquery.js file by applying any minification
        //that is desired via the optimize: setting above.
        {
            name: "require-jquery"
        },

        //Optimize the application files. Exclude jQuery since it is
        //included already in require-jquery.js
        {
            name: "main",
            exclude: ["jquery"]
        }
    ]
})

This file is used to tell r.js how to build the application. There is help on http://requirejs.org/docs/optimization.html about the file. Basically r.js will look at the main module, and trace all of it’s dependencies, minify them, and combine them for you into one fun happy little minified package at your dir from the app.build.js file.

To run the optimizer in Java, go to the file above the root of app where r.js is located and use the command…

java -cp c:/path/to/rhino/js.jar;c:/path/to/compiler/compiler.jar; org.mozilla.javascript.tools.shell.Main r.js -o "c:/path/to/your/app/scripts/app.build.js"

If you are using node, then run this command…

node r.js app/scripts/app.build.js

And next thing you know, you’ll have an app-build folder with a shiny new minifed version of your app. The node optimizer is quite a bit faster than the java one, so I’d use that one.

So, that’s basically it! If you have any questions, let me know as I spent many hours figuring this thing out. The biggest help was when jQuery and UnderscoreJS both added AMD, and then once the Backbone AMD branches came out, it was much easier!

I have a simple working app called Savefavs up here at js.jcreamerlive.com and the source for it on Github. So take a look at that for a little more advanced stuff

I also have a Starter application here https://github.com/jcreamer898/RequireJS-Backbone-Starter that you can fork and use to build your own app.

Tagged with: