http://documentcloud.github.com/backbone/
MVC for js?
http://documentcloud.github.com/backbone/examples/todos/index.html

http://www.quora.com/What-are-some-good-resources-for-Backbone-js
this is hard! Rob used peepcode.com backbone screencast. Took him 2 weeks to do this.
Use IIS Express.
Areas
Added an area called Api

http://localhost:50363/api/productions/
namespace VidPub.Web.Areas.Api.Controllers{
public class ProductionsController : ApplicationController {
DynamicModel _productions;
public ProductionsController(ITokenHandler tokenStore):base(tokenStore) {
_productions = new Productions();
}
[HttpGet]
public ActionResult Index() {
return VidpubJSON(_productions.All());
}
}
}

Alt+J – in chrome to open downloads, then tab lots to show in folder
Added backbone-min.js from another branch https://github.com/documentcloud/backbone/tree/gh-pages
and underscore-min.js http://documentcloud.github.com/underscore/
VidPub/Productions - View
Backbone is Event-driven.
el is short for Element.
Reference an ID:
#production-list
Reference a class:
.production-list ?
ListView = Backbone.View.extend({
render: function () {
$(this.el).html("<h1>Hello!</h1>");
return this;
}
});
jQuery(function () {
var view = new ListView();
$("#production-list").html(view.render().el);
});
asdf
@Assets.Script("underscore-min.js")
@Assets.Script("backbone-min.js")
@Assets.Script("production-admin.js")
<div id="production-list"></div>

and refactoring:
ListView = Backbone.View.extend({
initialize: function () {
this.template = $("#listTemplate");
},
render: function () {
var html = this.template.tmpl({ name: "Tekpub" });
$(this.el).html(html);
return this;
}
});
jQuery(function () {
var view = new ListView();
$("#production-list").html(view.render().el);
});
view:
<script type = "text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
@Assets.Script("underscore-min.js")
@Assets.Script("backbone-min.js")
@Assets.Script("production-admin.js")
<script id="listTemplate" type="text/html">
<h1>${name}</h1>
</script>
<div id="production-list"></div>

so all he’s doing here is pushing html into the dom.
ViewEvents
But its part of the template and not available when the page loads.

View:
<script type = "text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
@Assets.Script("underscore-min.js")
@Assets.Script("backbone-min.js")
@Assets.Script("production-admin.js")
<script id="listTemplate" type="text/html">
<h1>${name}</h1>
<a href="#">Click me!</a>
</script>
<div id="production-list"></div>
js
ListView = Backbone.View.extend({
initialize: function () {
this.template = $("#listTemplate");
},
events: {
"click a:": "clickityClick"
},
clickityClick: function () {
alert("I been clicked");
},
render: function () {
var html = this.template.tmpl({ name: "Tekpub" });
$(this.el).html(html);
return this;
}
});
jQuery(function () {
var view = new ListView();
$("#production-list").html(view.render().el);
});
here is an example of el not wired up.. and event doesn’t work:
ListView = Backbone.View.extend({
initialize: function () {
this.template = $("#listTemplate");
},
events: {
"click a:": "clickityClick"
},
clickityClick: function () {
alert("I been clicked");
},
render: function () {
var html = this.template.tmpl({ name: "Tekpub" });
//$(this.el).html(html);
//sidestep el completly and getting jQuery to patch in the template
$("#production-list").html(html);
return this;
}
});
jQuery(function () {
var view = new ListView();
//$("#production-list").html(view.render().el);
view.render();
});
Working With Data
Production = Backbone.Model.extend({});
ListView = Backbone.View.extend({
initialize: function () {
this.template = $("#listTemplate");
},
events: {
"click a:": "clickityClick"
},
clickityClick: function () {
alert("I been clicked");
},
render: function () {
var html = this.template.tmpl(this.model.toJSON());
$(this.el).html(html);
return this;
}
});
jQuery(function () {
var production = new Production({ name: "MVC3!" });
var view = new ListView({model: production});
$("#production-list").html(view.render().el);
});

pub/sub
binding/subscribing to the change event on this.clickityClick ** careful to use this inside function, otherwise js will look globally in the global window object.
Also setting model to a global variable so we can work with it:

Manually resetting the title to Peaches in chrome.
Bind our change event to render action in the view
Production = Backbone.Model.extend({});
ListView = Backbone.View.extend({
initialize: function () {
this.template = $("#listTemplate");
this.model.bind("change", this.render);
window.ViewModel = this.model;
},
events: {
"click a:": "clickityClick"
},
clickityClick: function () {
alert("I been clicked");
},
render: function () {
var html = this.template.tmpl(this.model.toJSON());
$(this.el).html(html);
return this;
}
});
jQuery(function () {
var production = new Production({ name: "MVC3!" });
listView = new ListView({model: production});
$("#production-list").html(listView.render().el);
});
then in chrome:
listView.model.set({name:"EF"})

the this in this.model.toJSON() keyword changes scope when fired outside of a view such as on a model or a collection
userscore library has a handy bindAll library which resets scope of this to the current view.

Production = Backbone.Model.extend({});
ListView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#listTemplate");
this.model.bind("change", this.render);
window.ViewModel = this.model;
},
events: {
"click a:": "clickityClick"
},
clickityClick: function () {
alert("I been clicked");
},
render: function () {
var html = this.template.tmpl(this.model.toJSON());
$(this.el).html(html);
return this;
}
});
jQuery(function () {
var production = new Production({ name: "MVC3!" });
listView = new ListView({model: production});
$("#production-list").html(listView.render().el);
});
Backbone collections
What if we need more than 1 model.
Production = Backbone.Model.extend({});
Productions = Backbone.Collection.extend({
model : Production,
url: "/api/productions"
});


these are backbone models.

actual model
reset the view to use a collection instead of a model.
“reset” whenever data is firehosed into collection.
- page is going to load
- view will be instantiated with empty collection
- el is going to be specified
- fetch is going to be called
- once fetch is completed, collection will fire its reset event
- which will fire the render event
- render will then push the templated data into el

productions.reset([{Title:"One"}, {Title:"Two"}]) - this will changed the data on the screen as event driven will fire.
Production = Backbone.Model.extend({});
Productions = Backbone.Collection.extend({
model : Production,
url: "/api/productions"
});
productions = new Productions();
ListView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#listTemplate");
//this.model.bind("change", this.render);
this.collection.bind("reset", this.render);
//window.ViewModel = this.model;
},
// events: {
// "click a:": "clickityClick"
// },
// clickityClick: function () {
// alert("I been clicked");
// },
render: function () {
//var html = this.template.tmpl(this.model.toJSON());
var html = this.template.tmpl(this.collection.toJSON());
$(this.el).html(html);
return this;
}
});
jQuery(function () {
//var production = new Production({ name: "MVC3!" });
//listView = new ListView({model: production});
listView = new ListView({ collection: productions, el: "#production-list" });
//$("#production-list").html(listView.render().el);
productions.fetch();
});
Backbone router
24mins in.
using callback function success
Production = Backbone.Model.extend({});
Productions = Backbone.Collection.extend({
model : Production,
url: "/api/productions"
});
productions = new Productions();
ListView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#listTemplate");
this.collection.bind("reset", this.render);
},
render: function () {
var html = this.template.tmpl(this.collection.toJSON());
$(this.el).html(html);
return this;
}
});
var ProductionAdmin = Backbone.Router.extend({
initialize: function () {
listView = new ListView({ collection: productions, el: "#production-list" });
},
routes: {
"": "index",
"edit/:id": "edit"
},
index: function () {
listView.render();
},
edit: function (id) {
alert("hello");
//grab the model from the production
$("#production-list").html("This id is " + id);
//pass it to an editor
//listView.render();
}
});
jQuery(function () {
productions.fetch({
success: function () {
alert("hello2");
//create the router
window.app = new ProductionAdmin();
Backbone.history.start();
},
error: function () {
//display a nice error page
alert("error");
}
});
});
editing: app.navigate("edit/1",true)

#allows back button to work, and hyperlinking back to a certain page to work..all with ajax. Power of backbone!
Working with Data
<a href="#" id="${ID}">${Title}</a>
not using this.. instead HTML5 data storage:

Production = Backbone.Model.extend({});
Production = Backbone.Model.extend({
idAttribute : "ID"
});
Productions = Backbone.Collection.extend({
model : Production,
url: "/api/productions"
});
productions = new Productions();
ListView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#listTemplate");
this.collection.bind("reset", this.render);
},
events: {
"click a": "showForm"
},
showForm: function (evt) {
//get tje ID that was clicked
var id = $(evt.currentTarget).data('production-id');
//navigate
app.navigate("edit/" + id, true);
return false;
},
render: function () {
var html = this.template.tmpl(this.collection.toJSON());
$(this.el).html(html);
return this;
}
});
FormView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#productionFormTemplate");
},
render: function () {
var html = this.template.tmpl(this.model.toJSON());
$(this.el).html(html);
return this;
}
});
var ProductionAdmin = Backbone.Router.extend({
initialize: function () {
listView = new ListView({ collection: productions, el: "#production-list" });
formView = new FormView({ el: "#production-form" });
},
routes: {
"": "index",
"edit/:id": "edit"
},
index: function () {
listView.render();
},
edit: function (id) {
listView.render();
$(formView.el).empty();
//grab the model from the production
var model = productions.get(id);
formView.model = model;
formView.render();
//$("#production-list").html("This id is " + id);
//pass it to an editor
//listView.render();
}
});
jQuery(function () {
productions.fetch({
success: function () {
//create the router
window.app = new ProductionAdmin();
Backbone.history.start();
},
error: function () {
//display a nice error page
alert("error");
}
});
});
@{
ViewBag.Title = "Productions";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<script type = "text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
@Assets.Script("underscore-min.js")
@Assets.Script("backbone-min.js")
@Assets.Script("production-admin.js")
<script id="listTemplate" type="text/html">
<li>
<a href="#" data-production-id="${ID}">${Title}</a>
</li>
</script>
<script id="productionFormTemplate" type="text/html">
<form id="productionForm">
<input type="text" id="Title" name="Title" value="${Title}" />
</form>
</script>
<div class="column span-5 colborder">
<div id="production-list"></div>
</div>
<div class="column span-14">
<div id="production-form"></div>
</div>
Form Binding
we want to bind the form to the model.. we can .. using events. will wire ourselves.. people working on modelbinders.

showing the model being changed by the view.
formView.model.get("Title")
this should be automatic – use Derik Baily’s modelbinding.
Helpers: copy existing Form.cshtml and rename to Template.cshtml
Nice button CSS, with jQuery template helper html.

nice form using form.css wired through the layout page.

date picker

just had to wire up the datepicker in formview, which wires us the jQueryUI datepicker. jqueryUI is wired up through _adminlayout.
FormView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#productionFormTemplate");
},
events: {
"change input": "updateModel",
"submit #productionForm": "save"
},
save: function () {
return false;
},
updateModel: function (evt) {
var field = $(evt.currentTarget);
var data = {};
data[field.attr('ID')] = field.val();
this.model.set(data);
},
render: function () {
var html = this.template.tmpl(this.model.toJSON());
$(this.el).html(html);
$(".datepicker").datepicker();
return this;
}
});
saving back to server:

reason we’re using IIS Express… cassini doesn’t always work with put.
rails like conventions


so data has been passed back to server via the formView.model.save() call or button press.
Production = Backbone.Model.extend({});
Production = Backbone.Model.extend({
idAttribute: "ID",
url: function () {
return this.isNew() ? "/api/productions/create" : "/api/productions/edit/" + this.get("ID");
},
});
Productions = Backbone.Collection.extend({
model : Production,
url: "/api/productions"
});
productions = new Productions();
ListView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#listTemplate");
this.collection.bind("reset", this.render);
},
events: {
"click a": "showForm"
},
showForm: function (evt) {
//get the ID that was clicked
var id = $(evt.currentTarget).data('production-id');
//navigate
app.navigate("edit/" + id, true);
return false;
},
render: function () {
var html = this.template.tmpl(this.collection.toJSON());
$(this.el).html(html);
return this;
}
});
FormView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.template = $("#productionFormTemplate");
},
events: {
"change input": "updateModel",
"submit #productionForm": "save"
},
save: function () {
this.model.save(
this.model.attributes,
{
success: function (model, response) {
model.collection.trigger("itemSaved", model);
},
error: function (model, response) {
model.trigger("itemError", "There was a problem saving " + model.get("Title"));
}
}
);
return false;
},
updateModel: function (evt) {
var field = $(evt.currentTarget);
var data = {};
data[field.attr('ID')] = field.val();
this.model.set(data);
},
render: function () {
var html = this.template.tmpl(this.model.toJSON());
$(this.el).html(html);
$(".datepicker").datepicker();
return this;
}
});
var ProductionAdmin = Backbone.Router.extend({
initialize: function () {
listView = new ListView({ collection: productions, el: "#production-list" });
formView = new FormView({ el: "#production-form" });
},
routes: {
"": "index",
"edit/:id": "edit"
},
index: function () {
listView.render();
},
edit: function (id) {
listView.render();
$(formView.el).empty();
//grab the model from the production
var model = productions.get(id);
formView.model = model;
formView.render();
//$("#production-list").html("This id is " + id);
//pass it to an editor
//listView.render();
}
});
jQuery(function () {
productions.fetch({
success: function () {
//create the router
window.app = new ProductionAdmin();
Backbone.history.start();
},
error: function () {
//display a nice error page
alert("error");
}
});
});
MVC3 modelbinders did their job and title in the incoming json is title in our p object.
but we’re not using statically typed objects.. can’t access via FormCollection (as it isn’t a post).. so have coded around:
Multiple types were found that match the controller named
http://ediblecode.com/blog/dot-net/asp-net-mvc/fixing-multiple-types-were-found-that-match-the-controller-named

ControllerBuilder.Current.DefaultNamespaces.Add("VidPub.Web.Controllers");
Date Problem
Wrote and app to test:
namespace MassiveDateTest {
class Program {
static void Main(string[] args) {
// test dates in massive for NZ locale
//do an insert
var table2 = new Productions();
//do it up - the new ID will be returned from the query
// fails with
// exec sp_executesql N'INSERT INTO Productions (Title,CreatedOn)
//VALUES (@0,@1)',N'@0 nvarchar(4000),@1 nvarchar(4000)',@0=N'Buck Fify Stuff',@1=N'30/08/2011'
var newID = table2.Insert(new { Title = "Buck Fify Stuff", CreatedOn = "21/08/2011" });
Console.WriteLine(newID);
var table = new Productions();
//grab all the productions
var productions = table.All();
foreach (var item in productions) {
// coming through as 29/08/2011 12:00:00
Console.WriteLine(item.Title + " " + item.CreatedOn);
}
// this works
//var newID = table2.Insert(new { Title = "Buck Fify Stuff", CreatedOn = "08/30/2011" });
}
}
public class Productions : DynamicModel {
public Productions() : base("VidPub", "Productions", "ID") {}
}
}
and massive:
public virtual DbCommand CreateInsertCommand(object o) {
DbCommand result = null;
var expando = o.ToExpando();
var settings = (IDictionary<string, object>)expando;
var sbKeys = new StringBuilder();
var sbVals = new StringBuilder();
var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2})";
result = CreateCommand(stub, null);
int counter = 0;
foreach (var item in settings) {
sbKeys.AppendFormat("{0},", item.Key);
sbVals.AppendFormat("@{0},", counter.ToString());
// DM hack dates
if (item.Key == "CreatedOn" || item.Key == "UpdatedOn" || item.Key == "ReleasedOn") {
string fulldate = item.Value.ToString();
string[] dateArray = fulldate.Split('/');
string day = dateArray[0];
string month = dateArray[1];
string year = dateArray[2];
string dateInUSFormat = month + "/" + day + "/" + year;
result.AddParam(dateInUSFormat);
} else
result.AddParam(item.Value);
counter++;
}
so got it writing a British date from C# and transforming into US in the insert code.
put into main project and got it working for

but time comes in on save:

create not working for some reason. **datepicker is in US format already.. need to set to UK