I have been doing a lot of reading lately on backbone.js. One of the key features I wanted to explore was it’s concept of Models and how it keeps the server side in sync with RESTful JSON endpoints.
What is backbone.js?
Backbone supplies structure to JavaScript-heavy 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 application over a RESTful JSON interface.
I started searching the internet to find an example in the .NET world, surely someone has an example already? Â I did run across a few posts that got me in the right direction:
The Backbone.js Todo List Sample, Refactored – Part 1 via @robconery
ASP.NET MVC3 RESTful application tutorial with Backbone.js – Part I
ASP.NET MVC3 RESTful application tutorial with Backbone.js – Part II
ASP.NET MVC3 RESTful application tutorial with Backbone.js – Part III
A Backbone.js demo app (Sinatra Backend)Â via ryandotsmith
The BitCandies series was more than I needed, the last article with Sinatra is what I was looking for. Â All I wanted to see was how backbone.js posts to the RESTful endpoints. Â If I could see that in action I knew I could do the same with MVC3 JsonResults in a Controller.
The chat demo:
This demo uses MVC3 Controllers as the RESTful JSON endpoints utilizing the Json ActionResult. I also made a MVC3 Model called Message to keep the client and server-side parity. I cheated with persistence using a static class, no database needed to run the demo. The original demo I forked from used setInterval to poll the server for new messages. This worked but for only one client. I wanted this to work like a real chat app would, so I replaced that with SignalR to let the clients know new messages have arrived and to update their data. I know I could replace the Controllers with SignalR all together but this is a demo to show how to use all the pieces, not a best practices.
/Views/Shared/_Layout.cshtml(script references needed)
......... <script src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/underscore.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/backbone.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.signalR.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script> ......... </body> <script src="@Url.Content("~/Scripts/application.js")" type="text/javascript"></script> .........
/Scripts/application .js (this is basically the single page app initializer for this example)
// SignalR Proxy created on the fly var chat = $.connection.chat; var Message = Backbone.Model.extend({}); var MessageStore = Backbone.Collection.extend({ model: Message, url: '/messages' }); var messages = new MessageStore; var MessageView = Backbone.View.extend({ events: { "submit #chatForm" : "handleNewMessage" } , handleNewMessage: function(data) { var inputField = $('input[name=newMessageString]'); messages.create({ content: inputField.val() }); //signalr call to server chat.send("dummy message, just signaling") .done(function () { console.log('Success!') }) .fail(function (e) { console.warn(e); }) inputField.val(''); } , render: function() { var data = messages.map(function(message) { return message.get('content') + 'n'}); var result = data.reduce(function(memo,str) { return memo + str }, ''); $("#chatHistory").text(result); return this; } }); messages.bind('add', function(message) { messages.fetch({success: function(){view.render();}}); }); var view = new MessageView({el: $('#chatArea')}); //replaced with SignalR //setInterval(function(){ // messages.fetch({success: function(){view.render();}}); //},10000) // Declare a function on the chat hub so the server can invoke it chat.reloadMessages = function (message) { //server callback, reload messages from server via backbone! messages.fetch({ success: function () { view.render(); } }); }; // Start the connection $.connection.hub.start(); //get any messages on load, .fetch is backbone.js working here messages.fetch({ success: function () { view.render(); } });
/Models/MessageModels.cs
namespace BackboneMVC3SignalR.Models { public class Message { public string content { get; set; } } }
/Controllers/MessagesController.cs
namespace BackboneMVC3SignalR.Controllers { public class MessagesController : Controller { // // GET: /Messages/ public ActionResult Index() { List<Message> s = new List<Message>(); if (GlobalVariables.Messages != null) { s = GlobalVariables.Messages; } return Json(s, JsonRequestBehavior.AllowGet); } [HttpPost] public ActionResult Index(string content) { List<Message> s = new List<Message>(); if (GlobalVariables.Messages != null) { s = GlobalVariables.Messages; } s.Add(new Message{ content = content}); GlobalVariables.Messages = s; return Json(s, JsonRequestBehavior.AllowGet); } } }
/Hubs/Chat.cs
namespace BackboneMVC3SignalR.Hubs { public class Chat : Hub { public void Send(string message) { // Call the reloadMessages method on all clients Clients.reloadMessages(message); } } }
Full working demo can be found on GitHub.