Skip to content

Instantly share code, notes, and snippets.

@joeriks
Created January 19, 2012 14:34
Show Gist options
  • Save joeriks/1640324 to your computer and use it in GitHub Desktop.
Save joeriks/1640324 to your computer and use it in GitHub Desktop.
Restful editable list with knockout and mvc3
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute("RestfulControllers", "api/{controller}", new
{
controller = "Home",
action = RestfulActionAttribute.RestfulActionName,
id = UrlParameter.Optional
});
context.MapRoute("RestfulControllers_Id", "api/{controller}/{id}", new
{
controller = "Home",
action = RestfulActionAttribute.RestfulActionName,
id = UrlParameter.Optional
})
}
(function () {
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
var url = "/api/categories";
function Model(data) {
this.Id = ko.observable(data.Id);
this.Name = ko.observable(data.Name);
}
function ModelListViewModel() {
// Data
var self = this;
self.tags = ko.observableArray([]);
self.newModelName = ko.observable();
// Operations
self.addModel = function () {
$.ajax({
url: url,
type: "POST",
data: {
Name: self.newModelName()
},
success: function (post) {
self.tags.push(new Model({
Name: self.newModelName(),
Id: post
}));
self.newModelName("");
}
});
};
self.removeModel = function (model) {
$.ajax({
url: url,
type: "DELETE",
data: model,
success: function (data) {
self.tags.destroy(model);
}
});
};
self.saveModel = function (model) {
$.ajax({
url: url,
type: "PUT",
data: model,
success: function (data) {
}
});
};
$.getJSON(url, function (allData) {
var mappedModels = $.map(allData, function (item) { return new Tag(item); });
self.tags(mappedModels);
});
}
$.ajaxSetup({
cache: false
});
$(document).ready(function () {
ko.applyBindings(new ModelListViewModel(),document.getElementById('edit-tags'));
});
})();
// handles uri :
// /api/categories [get]: all
// /api/categories/{id} [get/post/put/delete]: single with id
namespace MyApp.UI.Areas.Api.Controllers
{
public class CategoriesController : RESTController<Category>
{
public CategoriesController()
: base(MyApp.Current().Categories)
{
}
}
}
using System;
using System.Web.Mvc;
using MyApp.UserRequestContext;
namespace MyApp.UI.Areas.Api.Controllers
{
public abstract class RESTController<T> : Controller where T : class, new()
{
readonly ICRUDable<T> _crudResource;
protected RESTController(ICRUDable<T> crudableResouce)
{
_crudResource = crudableResouce;
}
[HttpGet]
[RestfulAction]
public ActionResult Get(int? id=null)
{
if (id == null)
return Json(_crudResource.All(), JsonRequestBehavior.AllowGet);
else
return Json(_crudResource.Get((int)id), JsonRequestBehavior.AllowGet);
}
[HttpDelete]
[RestfulAction]
public ActionResult Delete(int id)
{
_crudResource.Delete(id);
return Json(true);
}
[HttpPut]
[RestfulAction]
public ActionResult Put()
{
var model = _crudResource.Get(Convert.ToInt32(Request.Form["Id"]));
TryUpdateModel(model);
_crudResource.Update(model);
return Json(true);
}
[HttpPost]
[RestfulAction]
public ActionResult Post()
{
var model = new T();
TryUpdateModel(model);
return Json(_crudResource.Insert(model));
}
}
}
using System.Reflection;
using System.Web.Mvc;
// found at http://stackoverflow.com/questions/8610451/mvc3-restful-api-routing-http-verb-handling
// thanks Lucero
namespace MyApp.UI.Areas.Api.Controllers
{
public sealed class RestfulActionAttribute : ActionNameSelectorAttribute
{
internal const string RestfulActionName = "<<REST>>";
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
return actionName == RestfulActionName;
}
}
}
<html>
<head>
<title></title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js"></script>
<script type='text/javascript' src='/dev/knockout/knockout-2.0.0.js'></script>
<script type='text/javascript' src='/dev/knockout/tags.js'></script>
</head>
<body>
<script type='text/javascript' src='/reports/tabs/tags/categories/categories.js'></script>
<h3>
Categories</h3>
<div id="edit-categories">
<form data-bind="submit: addCategory" >
Lägg till kategori:
<input data-bind="value: newCategoryName" />
<button type="submit">
Add new</button>
</form>
<ul class="edit-items" data-bind="foreach: categories, visible: categories().length > 0">
<li>
<input type="hidden" data-bind="value: Id" />
<input data-bind="value: Name" />
<a href="#" data-bind="click: $parent.removeCategory">Delete</a> <a href="#" data-bind="click: $parent.saveCategory">
Save changes</a> </li>
</ul>
</div>
</body>
</html>
@joeriks
Copy link
Author

joeriks commented Jan 20, 2012

Forks and comments are warmly welcome.

@joeriks
Copy link
Author

joeriks commented Jan 21, 2012

In IIS Express you might need to do this http://learn.iis.net/page.aspx/901/iis-express-faq/ to enable DELETE and PUT (if you get 404's on delete and save)

@danieleli
Copy link

Hi. I think u might find similar functionality with ms webapi nuget package.

Also, knockout works great with mongodb. Don't want to try installing mongo? No worries. Mongohq.com has u up and running with micro instance and restful crud in minutes!

Niether is as fun As designing with generics though. :)

  • danieleli

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment