DataGrid View with "CRUD operations" using Dojo DataGrid, JsonRest Store, Entity Framework, SQL Server, ASP.NET MVC Web API
DataGrid View with "CRUD operations" using Dojo DataGrid, JsonRest Store, Entity Framework, SQL Server, ASP.NET MVC Web API
Table of contents
Introduction
Dojo Toolkit is an open source modular JavaScript library (or more specifically JavaScript toolkit) designed to ease the rapid development of cross-platform, JavaScript/Ajax-based applications and web sites and provides some really powerful user interface features (Dojo Toolkit). One of the most powerful Dojo tools is DataGrid
(DataGrid demo).
I wanted to use Dojo DataGrid
with Entity Framework and ASP.NET MVC, but I couldn't find any complete sample about it. This article walks-through the process of creating a Dojo DataGrid
to perform CRUD operations on the entities.
Creating Blog Model
This demo uses an ASP.NET Web API Project.
This project use Entity Framework Database First approach. But this isn't the point, you could also use Entity Framework Code First or Model First. Here, you could find an introduction to Database First development using Entity Framework. Database First allows you to reverse engineer a model from an existing database. You could use the article until you've got your model, your classes and your database in place, nothing more. We will make our controllers and views. Your Model and Database should be something like this:

Home/Index View
Home/Index View should contain all the codes below:
Dojo Data Grid
You could see a complete article about the code below in here.
As a point, this is so important that you use idProperty: "Id"
in both JsonRest, Memory
because Dojo DataGrid
uses id
as idProperty
but our blog id is Id
. This wastes my time. Hopefully, this tip can save someone else some time later.
dojo.query("body").addClass("claro");
adds "claro
" theme to Grid
and grid.canSort = function () { return false };
disables sorting of the grid
because we didn't write any code in our controller to support it. You could find about sorting and paging in here.
<link rel="stylesheet"
href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/resources/dojo.css" />
<link rel="stylesheet"
href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dijit/themes/claro/claro.css" />
<link rel="stylesheet"
href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojox/grid/resources/Grid.css" />
<link rel="stylesheet"
href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojox/grid/resources/claroGrid.css" />
<!-- load dojo and provide config via data attribute -->
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js"
data-dojo-config="async: true, isDebug: true, parseOnLoad: true">
</script>
<script>
var myStore, dataStore, grid;
require([
"dojo/store/JsonRest",
"dojo/store/Memory",
"dojo/store/Cache",
"dojox/grid/DataGrid",
"dojo/data/ObjectStore",
"dojo/query",
"dijit/form/Button",
"dojo/domReady!"
], function (JsonRest, Memory, Cache, DataGrid, ObjectStore, query, Button, domReady) {
myStore = Cache(JsonRest({ target: "/Api/Blog/",
idProperty: "Id" }), Memory({ idProperty: "Id" }));
grid = new DataGrid({
store: dataStore = ObjectStore({ objectStore: myStore }),
structure: [
{ name: "Blog Id", field: "Id", width: "50px" },
{ name: "Title", field: "Title", width: "200px" },
{ name: "Blogger Name",
field: "BloggerName", width: "200px" }
]
}, "grid"); // make sure you have a target HTML element with this id
grid.startup();
dojo.query("body").addClass("claro");
grid.canSort = function () { return false };
});
</script>
<div style="height: 300px; width: 600px; margin: 10px;">
<div id="grid">
</div>
</div>
The idea of code below came from Dojo Grid - Switching between editable and not editable, with more functionality. You should ask why Edit mode and Add/Remove mode are separated. It is because if "Add/Remove" be in Edit mode, you could add a blog and before saving, you could edit it but the server doesn't assign an Id to blog yet and an error will occur. Also Remove is with Add, because you could remove a newly added blog before saving it.
<div id="normalMode">
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var editButton = new Button({
label: "Edit",
onClick: function () {
editMode();
}
}, "editButton");
});
</script>
<button id="editButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var addRemoveButton = new Button({
label: "Add / Remove",
onClick: function () {
addRemoveMode();
}
}, "addRemoveButton");
});
</script>
<button id="addRemoveButton">
</button>
</div>
<div id="editMode" class="dijitHidden">
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var saveButton = new Button({
label: "Save",
onClick: function () {
saveTable();
}
}, "saveButton");
});
</script>
<button id="saveButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var cancelEditButton = new Button({
label: "Cancel",
onClick: function () {
cancelTable();
}
}, "cancelEditButton");
});
</script>
<button id="cancelEditButton">
</button>
</div>
<div id="addRemoveMode" class="dijitHidden">
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var saveAddRemoveButton = new Button({
label: "Save",
onClick: function () {
saveTable();
}
}, "saveAddRemoveButton");
});
</script>
<button id="saveAddRemoveButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var addButton = new Button({
label: "Add New Blog",
onClick: function () {
addBlog();
}
}, "addButton");
});
</script>
<button id="addButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var removeButton = new Button({
label: " Remove Selected Rows",
onClick: function () {
removeBlog();
}
}, "removeButton");
});
</script>
<button id="removeButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var cancelAddRemoveButton = new Button({
label: "Cancel",
onClick: function () {
cancelTable();
}
}, "cancelAddRemoveButton");
});
</script>
<button id="cancelAddRemoveButton">
</button>
</div>
<div id="message">
</div>
<script>
function addBlog() {
var newBlog = { Title: "New Title",
BloggerName: "New Blogger Name" };
dataStore.newItem(newBlog);
}
function removeBlog() {
var items = grid.selection.getSelected();
if (items.length) {
dojo.forEach(items, function (selectedItem) {
if (selectedItem !== null) {
dataStore.deleteItem(selectedItem);
}
});
}
}
function saveTable() {
if (grid.edit.isEditing()) {
grid.edit.apply();
}
if (dataStore.isDirty()) {
dataStore.save();
}
onSaveComplete();
}
function cancelTable() {
if (grid.edit.isEditing()) {
grid.edit.apply();
}
dataStore.revert();
normalMode();
}
function onSaveComplete() {
dojo.byId("message").innerHTML = ("Save done.");
normalMode();
}
function normalMode() {
var theStructure = grid.structure;
theStructure[1].editable = false;
theStructure[2].editable = false;
grid.set('structure', theStructure);
dojo.removeClass("normalMode", "dijitHidden");
dojo.addClass("editMode", "dijitHidden");
dojo.addClass("addRemoveMode", "dijitHidden");
}
function editMode() {
var theStructure = grid.structure;
theStructure[1].editable = true;
theStructure[2].editable = true;
grid.set('structure', theStructure);
//Clear any previous messages
dojo.byId("message").innerHTML = ("");
dojo.removeClass("editMode", "dijitHidden");
dojo.addClass("normalMode", "dijitHidden");
dojo.addClass("addRemoveMode", "dijitHidden");
}
function addRemoveMode() {
//Clear any previous messages
dojo.byId("message").innerHTML = ("");
dojo.removeClass("addRemoveMode", "dijitHidden");
dojo.addClass("normalMode", "dijitHidden");
dojo.addClass("editMode", "dijitHidden");
}
</script>
BlogController
As Dojo sends and receives JSON data to perform CRUD operations on the entities, so we need RESTful service within an ASP.NET MVC. We use API controller to make our RESTful service. Because we need Json as out put, we must add the following codes to "App_Start/WebApiConfig.cs" to force API controller return Json as output:
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes
.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
and because sometimes Json failed to serialize the response in Web API, we must add the following codes:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Adding BlogController

When you click on Add, "BlogController.cs" will made and it must contains the following code that generated automatically. Here you could find complete articles about Web-Api and API Controller.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using DojoDataGrid.Models;
namespace DojoDataGrid.Controllers
{
public class BlogController : ApiController
{
private BloggingContext db = new BloggingContext();
// GET api/Blog
public IEnumerable<Blog> GetBlogs()
{
return db.Blogs.AsEnumerable();
}
// GET api/Blog/5
public Blog GetBlog(long id)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return blog;
}
// PUT api/Blog/5
public HttpResponseMessage PutBlog(long id, Blog blog)
{
if (ModelState.IsValid && id == blog.Id)
{
db.Entry(blog).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
// POST api/Blog
public HttpResponseMessage PostBlog(Blog blog)
{
if (ModelState.IsValid)
{
db.Blogs.Add(blog);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, blog);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = blog.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
// DELETE api/Blog/5
public HttpResponseMessage DeleteBlog(long id)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
db.Blogs.Remove(blog);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK, blog);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
As you can see, the BlogController
performs "GET
/POST
/PUT
/DELETE
" in a single URL "/Api/Blog/".
POST
used to adding new blogPUT
used to editing a blogGET
used to sending a JSON data to grid containing all blogs or a blogDELETE
used to deleting a blog
If any error occurred, an error message will send with a Json data to grid.
See in Action
Now it's time to see the result. Build the solution and edit some blogs and add/remove some others.
As you could see in fireBug data will send or request throw Json REST.
References
- The official Dojo Toolkit site. You can get a copy of Dojo as well as the API documentation here:
http://www.dojotoolkit.org - Connecting a Store to a
DataGrid
:
http://dojotoolkit.org/documentation/tutorials/1.9/store_driven_grid/ - Database First development using Entity Framework:
http://msdn.microsoft.com/en-us/data/jj206878.aspx - ASP.NET web-api:
http://www.asp.net/web-api - Dojo Grid - Switching between editable and not editable:
http://stackoverflow.com/questions/3703573/dojo-grid-switching-between-editable-and-not-editable - dijit/form/Button:
http://dojotoolkit.org/reference-guide/1.9/dijit/form/Button.html - How do I get ASP.NET Web API to return JSON instead of XML:
http://stackoverflow.com/questions/9847564/how-do-i-get-asp-net-web-api-to-return-json-instead-of-xml-using-chrome - Failed to serialize the response in Web API:
http://stackoverflow.com/questions/12641386/failed-to-serialize-the-response-in-web-api