ExtJS and ASP.NET MVC 3: CRUD DataGrid

This short tutorial will walk though the implementation of DataGrid using ExtJS 3.3.1 and ASP.NET MVC 3. In this tutorial I focus more on the integration of the front-end, so you will not find any database code. Whenever we deal with data we usually create, read/retrieve, update or delete them. ExtJS provides a great data grid component and the ability to perform CRUD operation with very little coding. I used JSON as data format in the example below.

ExtJS DataGrid
ExtJS DataGrid

Let’s start with the server-side by developing the data model and controller.

Controller:

    public class ContactController : Controller
    {
        //
        // GET: /Contact/

        public ActionResult Index()
        {
            return View();
        }

        public JsonResult Load()
        {
            var contact = new List<Contact> {
                new Contact("Smith","95746325","smith@me.com"),
                new Contact("Adam","87291034","adam@me.com"),
                new Contact("Eve","98271345","eve@me.com"),
                new Contact("Chun Li","81728312","chun.li@me.com")
            };
            return Json(new
            {
                total = contact.Count,
                data = contact,
            }, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public JsonResult Create(List<Contact> data)
        {
            //insert Create code
            return Json(new
            {
                data = new Contact(data[0].Name, data[0].Phone, data[0].Email),
                success = true,
                message = "Create method called successfully"
            });
        }

        [HttpPost]
        public JsonResult Update(List<Contact> data)
        {
            //insert Update code
            return Json(new
            {
                success = true,
                message = "Update method called successfully"
            });
        }

        [HttpPost]
        public JsonResult Delete(List<string> data)
        {
            //insert Delete code
            return Json(new
            {
                success = true,
                message = "Delete method called successfully"
            });
        }
    }

Data Model:

    public class Contact
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }

        public Contact(string pName, string pPhone, string pEmail)
        {
            this.Id = System.Guid.NewGuid().ToString();
            this.Name = pName;
            this.Phone = pPhone;
            this.Email = pEmail;
        }

        public Contact() { }
    }

ExtJS:

Now, you move on to the view and work on the ExtJS. First, you define the record type which matches the server-side object.

        var Contact = Ext.data.Record.create([
            {
                name: 'Id',
                type: 'string'
            }, {
                name: 'Name',
                type: 'string'
            }, {
                name: 'Phone',
                type: 'string'
            }, {
                name: 'Email',
                type: 'string'
            }
        ]);

Now you need to setup the Writer and Reader

        var writer = new Ext.data.JsonWriter({
            encode: false,
            listful: true,
            writeAllFields: true
        });

        var reader = new Ext.data.JsonReader({
            totalProperty: 'total',
            successProperty: 'success',
            idProperty: 'Id',
            root: 'data',
            messageProperty: 'message'  // <-- New "messageProperty" meta-data
        }, Contact);

Then, setup a proxy to define the connection to the controller.

        var proxy = new Ext.data.HttpProxy({
            api: {
                read: '/Contact/Load',
                create: '/Contact/Create',
                update: '/Contact/Update',
                destroy: '/Contact/Delete'
            },
            headers: { 'Content-Type': 'application/json; charset=UTF-8' }
        });

Hooks the above components (reader, writer, proxy) to the store.

        var store = new Ext.data.Store({
            id: 'user',
            proxy: proxy,
            reader: reader,
            writer: writer,
            autoSave: false
        });

Add the data grid declaration

        var grid = new Ext.grid.GridPanel({
            store: store,
            columns: [
                { header: "Name",
                    width: 170,
                    sortable: true,
                    dataIndex: 'Name',
                    editor: {
                        xtype: 'textfield',
                        allowBlank: false
                    }
                },
                { header: "Phone No.",
                    width: 160,
                    sortable: true,
                    dataIndex: 'Phone',
                    editor: {
                        xtype: 'textfield',
                        allowBlank: false
                    }
                },
                { header: "EMail",
                    width: 170,
                    sortable: true,
                    dataIndex: 'Email',
                    editor: {
                        xtype: 'textfield',
                        allowBlank: false
                    }
                }
            ],
            plugins: [editor],
            title: 'Contacts DataGrid',
            height: 300,
            width: 510,
            tbar: [{
                iconCls: 'icon-user-add',
                text: 'Add Contact',
                handler: function () {
                    var e = new Contact({
                        Name: 'New Friend',
                        Phone: '(65) 89182736',
                        Email: 'new@google.com'
                    });
                    editor.stopEditing();
                    store.insert(0, e);
                    grid.getView().refresh();
                    grid.getSelectionModel().selectRow(0);
                    editor.startEditing(0);
                }
            }, {
                ref: '../removeBtn',
                iconCls: 'icon-user-delete',
                text: 'Remove Contact',
                handler: function () {
                    editor.stopEditing();
                    var s = grid.getSelectionModel().getSelections();
                    for (var i = 0, r; r = s[i]; i++) {
                        store.remove(r);
                    }
                }
            }, {
                iconCls: 'icon-user-save',
                text: 'Save All Modifications',
                handler: function () {
                    store.save();
                }
            }]
        });

Some observations:

  • When submitting data as JSON, set the headers “Content-Type” to “application/json” in the proxy object and set the encoding to false in the JsonWriter. If not, it will be treated as form submission.
  • Since I set the auto save to false. DataGrid will submit list of contact when there are 2 or more changes, however it will send a single object when there is 1 change. In order to make it consistent to send as list, set list full to true in the JsonWriter.
  • ASP.NET MVC 3 able to recognize the JSON without additional coding.

If you want to see the complete source code, download it from here. Hope it’s useful. 🙂

Happy hacking!

Advertisements

28 thoughts on “ExtJS and ASP.NET MVC 3: CRUD DataGrid”

  1. Thanks for the post. Just one question, when i try to create, update or delete the data parameter is always null. I am using VS 2008, with MVC version 1 so presume i am missing some of the additional coding you mentioned. Currently i can’t upgrade so if you could point me in the right direction for this extra code i would really appreciate it.

    1. Hi.. I’m afraid that there is no easy way out here for v1.0. in v3.0 the JSON handler is build-in.
      I have 2 references below,

      1. To use Ext.Direct. http://code.google.com/p/ext-direct-mvc/. Looks like this is a cleaner alternative.

      2. Code own JSON serializer. You can refer to example from the MVC team at http://aspnet.codeplex.com/releases/view/24471 – Look for “Rest for ASP.NET MVC” under Other Available Downloads.
      Look for the the “Product” solutions, you will see a project call EdmSample. There are 2 source files you can start with
      a. Global.asax.cs – you will see the custom format manager, FormatManager.Current = new MyFormatManager();
      b. Site.Master – you will the menu where it links to the sample using xml and json.
      This example looks experimental

      I hope that help. Do let me know if it works for your project.

  2. Thank you for sharing the source code, its helped. I have problem in ‘boolean’,’date’,’number’ types in “Contact” entity propertys, read and write Json data. ‘date’ can write, but cant read. ‘boolean’ and ‘number’ cant write and read. Why? How can I do to make this work?

    1. Hi spsp,

      Best is to convert the date into a format, as it goes down (json), that matches your format for the date data model on ExtJS 4. You won’t have a problem with reading and writing.

      Also, for your boolean and number, make sure to define its data type on the data model.

    2. Hi spsp,
      In ExtJS Date there is a special format for Microsoft AJAX serialized dates. It is “M$” and used as part of the Ext.data.Record.
      {
      name: 'BirthDate',
      type: 'date',
      dateFormat: 'M$'
      },

      I updated the project file and include boolean & int type in the data model. You can download from https://github.com/wswijaya/ExtJsDataGridMvcApp. Changes are in the model, controller and contact/Index.aspx.

      Btw, it seems there is a problem with createContextualFragment in IE9. I have not seen the update for the community version but I found the temporary workaround below. It is only applicable if you are using the HTML 5, if not remove the Html5 Doctype and no change required. For html 5, without the code, you will see error when you click the calendar button.
      if ((typeof Range !== "undefined") && !Range.prototype.createContextualFragment) {
      Range.prototype.createContextualFragment = function (html) {
      var doc = window.document;
      var container = doc.createElement("div");
      container.innerHTML = html;
      var frag = doc.createDocumentFragment(), n;
      while ((n = container.firstChild)) {
      frag.appendChild(n);
      }
      return frag;
      };
      }

      Hope that help….

  3. Hi Peter Kellner, thank you for offering me the result. By finding the relating source I can make this work now, because we will use it soon.

  4. How can I write Load Method to get params pass from js like below.
    store.load([params:{start:0,limit:5}]);
    Please help me. Thank you!

    1. hi spsp,
      in my example i set the header as json thus when you pass the params in the load the server-side unable to convert properly.
      if the purpose is just to pass the value then do JSON encode as shown below.
      store.load({ params: Ext.util.JSON.encode({ start: 0, limit: 25 }) });

      on the server side modify the load to accept the parameters

      public JsonResult Load(int? start, int? limit)
      {
      if (start.HasValue && limit.HasValue) // do something
      ...

      If you intend to use the pagingtoolbar. I think you need to customize the paging toolbar to pass json also. Look the doLoad() and modified it as below.
      I haven’t think of another simpler way.

      doLoad: function (start) {
      var o = {}, pn = this.getParams();
      o[pn.start] = start;
      o[pn.limit] = this.pageSize;
      if (this.fireEvent('beforechange', this, o) !== false) {
      this.store.load({ params: Ext.util.JSON.encode(o) });
      }
      },

      hope that helps.

      1. Thank you for your help! I do things you said above, and pass real total, then it can move to next page now. But the cursor which in PagingToolbar didnt change to 0+pageSize, because of in onLoad function o.params isn’t have start parameter, maybe this is caused by encoding params in store.load(params:…)
        what can I do about this?
        Quote:
        onLoad : function(store, r, o){
        if(!this.rendered){
        this.dsLoaded = [store, r, o];
        return;
        }
        var p = this.getParams();
        this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;

  5. Hi – this looks great. How much of it would need to be changed to make it work for ExtJS v4? Is there an updated project .zip available for that? Thanks for your time.

    1. hi, there is a bit of changes in how the object define, the data model (proxy, store, model) are defined and the roweditor is now part of the main API (Ext.grid.plugin.RowEditing). I haven’t updated the sample yet.

  6. Thanks for the reply mate. If you happen to update it perhaps you can reply in the thread so I can grab it. I’m in ‘framework evaluation’ mode at the moment 🙂

  7. Hi there, just became aware of your weblog through Google, and found that it’s really informative. I?m going to watch out for brussels. I?ll be grateful in case you proceed this in future. Lots of other folks will probably be benefited from your writing. Cheers!

  8. This is very good article. But can you please tell me how to implement paging in it. I am new to ext JS so please guide me how to implement paging in this sample

  9. Hi very good example!! Does anyone have a EXTJS 4 MVC + ASP.NET MVC 3.0 sample application. I have a project that will be done using MVC on both sides (client and server) and would like to see an example.

    Thanks a lot!

  10. Pingback: login extjs 4 mvc

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s