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)
[sourcecode language="html"]
………
<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>
………
[/sourcecode]
/Scripts/application .js (this is basically the single page app initializer for this example)
[sourcecode language="javascript"]
// 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(); } });
[/sourcecode]
/Models/MessageModels.cs
[sourcecode language="csharp"]
namespace BackboneMVC3SignalR.Models
{
public class Message
{
public string content { get; set; }
}
}
[/sourcecode]
/Controllers/MessagesController.cs
[sourcecode language="csharp"]
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);
}
}
}
[/sourcecode]
/Hubs/Chat.cs
[sourcecode language="csharp"]
namespace BackboneMVC3SignalR.Hubs
{
public class Chat : Hub
{
public void Send(string message)
{
// Call the reloadMessages method on all clients
Clients.reloadMessages(message);
}
}
}
[/sourcecode]
Full working demo can be found on GitHub.
