Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

DataGrid View with CRUD operations using Dojo DataGrid, JsonRest Store, Entity Framework, SQL Server, ASP.NET MVC Web API

0.00/5 (No votes)
7 Jul 2013 2  
DataGrid View with CRUD operations using Dojo DataGrid, JsonRest Store, Entity Framework, SQL Server, ASP.NET MVC Web API

331920/DataGridDemo.png

Table of contents

  1. Introduction
  2. Model
  3. View
  4. Controller
  5. See in action
  6. References

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:

331920/BlogModel.png

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>

331920/grid.png

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> 

normalMode

    <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>

editMode

    <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>

331920/addRemoveMode.png

    <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;  

See here and here for detail.

Adding BlogController

331920/BlogController.png

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 blog
  • PUT used to editing a blog
  • GET used to sending a JSON data to grid containing all blogs or a blog
  • DELETE 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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here