Phoenix Software

27.05.2009 MVC ASP.NET grid - Editable rows. Using html table, jQuery and Ajax

Business case

A product manager need to change a id, name and price for products he is responsible of. He would like to have a grid with those fields. All columns should be editable on the fly, without any post-backs. After editing a manager clicks a Save button to save all changed rows. MVC ASP.NET grid with editable rows

This business case is very similar to the previous article - MVC ASP.NET grid – Editable column(cell) using Ajax
Main difference is that the manager should be able to edit all cells in the table. We have several possible solutions here. Let’s consider them and find the best one.

Solution 1. Similar to the previous article.
  • Adding a callback to all cells in the table.
  • The callback function sends an ajax request on the server with a new cell’s value.
  • The server saves the value in a user’s session.
  • When a user clicks the Save button, a callback function sends an ajax request to save those values to a data store.
Disadvantages –
  • Ajax request is preformed every time a cell is changed. It is a small request but there would be a lot of them.
  • User session can expire and lost the data.
  • Every cell have a callback function. The html is much bigger and not so easy to read.
Solution 2. Working with data store directly.
The same as the first one but with saving a value directly to a data store.
Disadvantages –
  • Ajax request is preformed every time a cell is changed. It is a small request but there would be a lot of them.
  • There are a lot of small requests to a data store.
  • Every cell have a callback function. The html is much bigger and not so easy to read.
Solution 3. Working with rows.
  • Every row in a table have Edit and Save buttons.
  • All rows have a callback.
  • The callback function sends an ajax request on the server with new row’s values.
  • The server saves the values in a data store.
Disadvantages –
  • A user should click Edit first then Save after. It is not convenient when he needs to update a one column only, for instance - prices.
Solution 4. Saving a whole table at once.
  • Save button has a callback on click event.
  • The callback iterates all cells in the table and finds the values then send ajax request with those values to the server.
  • The server parses the values, creates a list of business objects and passes it to save in a data store.
Disadvantages – I didn’t find such.

So, let’s create a sample for Solution 4.

The data store, the model and the product service.
They are similar to the previous article - MVC ASP.NET grid – Editable column(cell) using Ajax

The products controller.
It performs parsing of JSON array strings with values and saves it to a data store.

 public class ProductController : Controller
    {
        //
        // GET: /Products/

        private List<Hashtable> ParseJson(string[] array, int rowsCount)
        {
            var result = new List<Hashtable>();

            List<string[]> list = new List<string[]>();
            foreach (var item in array)
            {
                int comma = item.IndexOf(',');
                list.Add(new string[] { item.Substring(0, comma), item.Substring(comma + 1) });
            }

            int i = 0;
            while (i < list.Count)
            {
                var dict = new Hashtable();
                int j = i + rowsCount;
                for (; i < j; i++)
                {
                    dict.Add(list[i][0], list[i][1]);
                }

                result.Add(dict);
            }

            return result;

        }

        public ActionResult Index()
        {
            return View(ProductService.Get());
        }

        public void Save(string[] inputs, int columnsCount)
        {
            List<Hashtable> r = ParseJson(inputs, columnsCount);

            List<Product> products = new List<Product>();
            foreach (var item in r)
            {
                Product p = new Product();
                p.ProductId = Convert.ToInt32(item["ProductId"]);
                p.Name = (string)item["Name"];
                p.Price = Convert.ToDouble(item["Price"]);

                products.Add(p);
            }
            ProductService.Save(products);
        }

    }

private List ParseJson(string[] array, int rowsCount) – parses array of strings to a list of key/value pairs. Each hashtable includes the pairs only for one business object. For instance, we have 6 strings – “ProductId,0”, “Name, Product 0”, “Price, 876,00”,“ProductId,1”, “Name, Product 1”, “Price, 901,20”. The resulted list would include 2 hashtables with 3 pairs.
public void Save(string[] inputs, int columnsCount) – gets array of strings from UI, calls ParseJson function, creates a list of products and saves it to a data store.

The products’ view.
It shows an html table with input elements inside its cells. After clicking on the Save button, it provides iterating through the table, collects values and sends them to a server by Ajax request.

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>

    <script type="text/javascript">
    
        function SaveAllClick() {

            var i = 0;
            var inputs = new Array();
            $("#ProductsTable").find("input").each(function() {
                inputs[i] = [this.id, this.value];
                i++;
            });

            var columnsCount = $("#ProductsTable").find("th").length;

            $.post("Product/Save", { inputs: inputs, columnsCount: columnsCount });

            Border();

            alert("Saved");
        }

        var prevRow;

        function Border(row) {
            if (prevRow != null) {
                $(prevRow).find("input").attr("style", "border-style: none");
            }
            if (row != null) {
                var i = $(row).find("input");
                i.attr("style", "border-style: inset");
            }
            prevRow = row;
        }
        
        
          
    </script>

    <h2>
        Index</h2>
    <table id="ProductsTable">
        <tr>
            <th>
                ProductId
            </th>
            <th style="width: 300px">
                Name
            </th>
            <th>
                Price
            </th>
        </tr>
        <% foreach (var item in Model)
           { %>
        <tr onclick="Border(this)">
            <td>
                <input id="ProductId" type="text" 
style="border-style: none" value="<%= item.ProductId %>" /> </td> <td> <input id="Name" type="text" style="border-style: none" value="<%= item.Name %>" /> </td> <td> <input id="Price" type="text" style="border-style: none"
value="<%= String.Format("{0:F}", item.Price) %>" /> </td> </tr> <% } %> </table> <p> <input id="SaveAll" type="button" value="Save" onclick="SaveAllClick()" /> </p> </asp:Content>

function SaveAllClick() – iterates through the table, collects values and sends them to a server by Ajax request.
function Border(row)- switch on/off a border around the edited row.

Conclusions.
It is really simple and flexible. It requires some more coding then with ASP.NET controls but gives you much more control over html rendering.
The example can be improved by automatic binding the products in Save method of the products controller using reflection. But it is out of the bounds of this sample and I’m sure you can do it easily without my help.

Take care,
Anton Venger

Should you have any questions, please, email - blogs@phoenixsoft.com.ua
Shout it
See Also:
MVC ASP.NET framework
MVC ASP.NET Cascading Dropdowns with AJAX
MVC ASP.NET grid – Editable column(cell) using Ajax


Most Popular:
MVC ASP.NET grid - Editable rows. Using html table, jQuery and Ajax
WPF TreeView
MVC ASP.NET Cascading Dropdowns with AJAX
MVC ASP.NET grid – Editable column(cell) using Ajax
MVC ASP.NET framework
Software performance improvement
How to make a web site
How to resolve a trouble with Delay Loaded Dll in Visual Studio 6.0
Изменить язык:    
Phoenix Software

Contact us

+38 063 592 81 31
+38 050 196 88 34