JavaScript Todos–Backbone with asp.net mvc

There are certainly plenty of articles, examples, etc… around creating the ‘Todos’ sample.  To learn more about different frameworks and a more complete example, check out Addy Osmani‘s examples here: http://addyosmani.github.com/todomvc/   This is a fantastic resource to learn about different available frameworks.

Most of these examples tend to use the localStorage.  Not that it’s a big deal, but I wanted to put together something very very simple that showed some connections between using Backbone.js to make calls to a backend server.  In this case, I have decided to use asp.net mvc.

The example uses IISExpress to be hosted.  To reduce some of the friction, there is no database, just a static List object on the server.  I am not handling edits in this simple example – just a GET call to show todos and POST call to add a new todo.

To get an example – you can get it on github https://github.com/sgentile/MvcBackbone

First, this is not a ‘how to use backbone’ nor a ‘how to use asp.net mvc’ – I’d probably fail you on both subject matters!  The goal here is to highlight the aspects of backbone that help in making the connection to the backend server.  Backbone is providing an abstraction over the ajax calls.  You certainly CAN use ajax directly if you want. 

So, let’s get started.   The ‘todo’ model is quite simple – see todo.js line 5 :

Todo = Backbone.Model.extend({
    // Default attributes for the todo.
    defaults: {
      content: "empty todo..."
    },
        // Ensure that each todo created has `content`.
    initialize: function() {
      if (!this.get("content")) {
        this.set({"content": this.defaults.content});
      }
    },
    
    url: "home/todos"
});

If you check the backbone.js document for Model url http://documentcloud.github.com/backbone/#Model-url

Returns the relative URL where the model’s resource would be located on the server. If your models are located somewhere else, override this method with the correct logic. Generates URLs of the form: "/[collection.url]/[id]", falling back to "/[urlRoot]/id" if the model is not part of a collection.

The key here is to let backbone know the url for Todos.  We will be making a ‘save’ call on our Todo model object when we add a new model  ( save: http://documentcloud.github.com/backbone/#Model-save ).  This will look for the url on the server:

the save will be a "create" (HTTP POST), if the model already exists on the server, the save will be an "update" (HTTP PUT).

 

The TodoFormView on line 38, handles connecting the forum submit call to our save function, which maps the form to the Todo model.  Calling ‘save’ directly on the model results in an ajax call – ie:

TodoFormView = Backbone.View.extend({
    initialize: function () {
        this.template = $("#formTemplate");
    },
    events: {
        "submit #todo-form": "save"
    },
    render: function () {
        var content = this.template.tmpl();
        $(this.el).html(content);
        return this;
    },
    save: function () {
        var val = this.$("input").val();
        var model = new Todo({ content: val });
        model.save();
        this.$("input").val('');
    }
});

The key items to see here is the save function call  – where we save the model – model.save();

So the ‘home/todos’ matches up to the HomeController Todos action – with is a new Todo, so therefore it cooresponds to the following:

        [HttpPost]
        public ActionResult Todos(Todo todo)
        {
            todo.id = Guid.NewGuid();
            TodoList.Add(todo);
            return Json(TodoList, JsonRequestBehavior.AllowGet);
        }

As you see, backbone is handling the underlying ajax POST calls to the server!

Obviously GET calls are equally as straightforward.  I have a collection of Todos defined:

Todos = Backbone.Collection.extend({
    model: Todo,
    url: "home/todos"
});

I am able to do a GET call with ‘fetch’ as seen below:

TodoListView = Backbone.View.extend({
    initialize: function () {
        _.bindAll(this, "render");
        this.collection.bind("change", this.render);
        this.collection.bind("add", this.render);
        this.collection.bind("fetch", this.render);

        todos.fetch({ add: true });
        
    },
    render: function () {
        //clear out the existing list to avoid "append" duplication
        $(this.el).empty();
        //use an array here rather than firehosing the DOM
        //perf is a bit better
        var els = [];
        //loop the collection...
        this.collection.models.forEach(function (todo) {
            //rendering a view for each model in the collection
            var view = new TodoItemView({ model: todo });
            //adding it to our array
            els.push(view.render().el);
        });
        //push that array into this View's "el"
        $(this.el).append(els);
        return this;
    }
});

The key here is the todos.fetch call.  You can learn more about collections and fetch here: http://documentcloud.github.com/backbone/#Collection-fetch

This is all for now, should be a good start – there is much to learn here obviously (ie bootstrapping). 

Next time, I hope to share more about using backbone with asp.net mvc and then add jquery mobile to the mix.  Thanks!

2 thoughts on “JavaScript Todos–Backbone with asp.net mvc

  1. Hi,

    Thanks for this article.

    I noticed that each time I would add a new item, it would actually do a postback.

    This is how I fixed your code:

    1. I removed the form in the template

    Type into the box and hit enter to add a todo:

    2. I changed the event binding for the save method to “keypress #new-todo”: “save”

    3. The save function looks like that now:

    save: function (e) {
    if (e.charCode != 13) return;
    var val = this.$(“input”).val();
    var model = new Todo({ content: val });
    todos.create(model);
    this.$(“input”).val(”);
    }

    Thank you for the article anyways, I am trying to learn Backbone and your code helped me better understand how things work.

    Cheers,

    Aymeric

  2. Excellent Aymeric !

    I went back and applied this and update the git to include it.

    I’ll make edits to the blog post to reflect this – much appreciated!

    (note, i did end up using e.which != 13 vs. charCode)

    Thanks!!!
    Steve

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s