Showing posts with label HTML Table. Show all posts
Showing posts with label HTML Table. Show all posts

Thursday, May 24, 2012

Show/hide detail in a table using jQuery and MVC 3

This is another simple example where we will be showing some record in a table and there will be some plus/ minus image in each row and on click of the image we will be showing some detail of the record. And we will toggle the images and data. To do that lets have the following view models-
namespace MVCRazor.ViewModel
{
    public class TableRowItemViewlModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public TableRowItemDetailViewlModel Detail { get; set; }
    }
    public class TableRowItemDetailViewlModel
    {
        public string Address { get; set; }
        public string Sex { get; set; }
        public string Nationality { get; set; }
    }
}
The model is simple and self explanatory. And we are going to use the following view to display the tabular data and the controller to read the data. In the sample we have taken some in memory object for data access-
namespace MVCRazor.Controllers
{
    public class TableRowDetailController : Controller
    {
        List<TableRowItemViewlModel> data = new List<TableRowItemViewlModel>();
        public TableRowDetailController()
        {
            data.AddRange(new List<TableRowItemViewlModel>(){
            new TableRowItemViewlModel(){ Id =1,Name="Name 1", Email ="Email 1", Detail=new TableRowItemDetailViewlModel(){Address="Address 1", Nationality="Nationality 1", Sex="Sex 1"}},
            new TableRowItemViewlModel(){ Id =2,Name="Name 2", Email ="Email 2", Detail=new TableRowItemDetailViewlModel(){Address="Address 2", Nationality="Nationality 2", Sex="Sex 2"}},
            new TableRowItemViewlModel(){ Id =3,Name="Name 3", Email ="Email 3", Detail=new TableRowItemDetailViewlModel(){Address="Address 3", Nationality="Nationality 3", Sex="Sex 3"}},
            new TableRowItemViewlModel(){ Id =4,Name="Name 4", Email ="Email 4", Detail=new TableRowItemDetailViewlModel(){Address="Address 4", Nationality="Nationality 4", Sex="Sex 4"}},
            new TableRowItemViewlModel(){ Id =5,Name="Name 5", Email ="Email 5", Detail=new TableRowItemDetailViewlModel(){Address="Address 5", Nationality="Nationality 5", Sex="Sex 5"}},
            new TableRowItemViewlModel(){ Id =6,Name="Name 6", Email ="Email 6", Detail=new TableRowItemDetailViewlModel(){Address="Address 6", Nationality="Nationality 6", Sex="Sex 6"}},
            new TableRowItemViewlModel(){ Id =7,Name="Name 7", Email ="Email 7", Detail=new TableRowItemDetailViewlModel(){Address="Address 7", Nationality="Nationality 7", Sex="Sex 7"}},
            new TableRowItemViewlModel(){ Id =8,Name="Name 8", Email ="Email 8", Detail=new TableRowItemDetailViewlModel(){Address="Address 8", Nationality="Nationality 8", Sex="Sex 8"}},
            new TableRowItemViewlModel(){ Id =9,Name="Name 9", Email ="Email 9", Detail=new TableRowItemDetailViewlModel(){Address="Address 9", Nationality="Nationality 9", Sex="Sex 9"}},
        });
        }

        public ActionResult Index()
        {
            return View((from c in data
                         select new TableRowItemViewlModel() { Id = c.Id, Name = c.Name, Email = c.Email }).ToList());
        }
    }
}
@model List<MVCRazor.ViewModel.TableRowItemViewlModel>
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<style>
.tbl {border:1px solid gray; }
.tbl td{padding:5px 10px 5px 10px; }
.tbl th{padding:5px 10px 5px 10px;background-color:Gray;color:White }
.pm{ cursor:pointer;}
.plus
{
    background:url('http://www.quimicasuiza.com/images/extras/plus-minus.gif') 0 -16px;
    display:block;
    width:16px;
    height:16px;
}
.minus
{
    background:url('http://www.quimicasuiza.com/images/extras/plus-minus.gif') 0 0;
    display:block;
    width:16px;
    height:16px;
}
.detail
{
    background-color:#d4d0d8;    
    padding:7px;
}
</style>
<table class="tbl" cellpadding="0" cellspacing="0">
<tr><th>&nbsp;</th><th>Id</th><th>Name</th><th>Email</th></tr>
@foreach (var row in Model)
{ 
    <tr><td><span class="pm plus"></span></td><td>@row.Id</td><td>@row.Name</td><td>@row.Email</td></tr>
}
</table>
The above controller and the view will display the data like below-
We are going to use the following script to retrieve data from the controller and show hide the details.
<script type="text/javascript">
    var colCount;
    $(document).ready(function () {
        colCount = $(".tbl tr:first").children().length;
        $("tr:odd").css("background-color", "#f0f3f4");
        $(".pm").click(function () {
            if ($(this).hasClass("plus")) {
                $(this).removeClass("plus").addClass("minus");
                if (!$(this).closest("tr").next().hasClass("detail")) {
                    getDetail($(this).closest("tr"));
                }
                else
                    $(this).closest("tr").next().show();
            }
            else {
                if ($(this).closest("tr").next().hasClass("detail"))
                    $(this).closest("tr").next().hide();
                $(this).removeClass("minus").addClass("plus");
            }
        });
    });
    function getDetail(row) {
        var rowNew = $("<tr class='detail'><td colspan=" + colCount + "></td></tr>");
        $.ajax({
            type: "get",
            timeout: 30000,
            data: "id=" + row.find("td:eq(1)").html(),
            url: '@Url.Action("GetDetail")',
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            success: function (result) {
                rowNew.find("td").append("<b>Address </b>: " + result.Address);
                rowNew.find("td").append("<br /><b>Sex </b>: " + result.Sex);
                rowNew.find("td").append("<br /><b>Nationality </b>: " + result.Nationality);
                row.after(rowNew);
            },
            error: function (a, b, c) {
                debugger;
            }
        });
    }
</script>
In this we are showing plus/minus button as span with CSS class pm. On click of the span we are checking whether it has a class named plus . Based on that in line 8 and 18 we are toggling the classes plus/minus.

If the line has class called plus, in the line 9 we are checking whether the next row of the container row has a CSS class called detail. Suppose it has that class then we are simply showing that row(in line 13). Otherwise we are calling a function called getDetail in line 10 by passing the current row. In that function we are building a row in memory with same columnspan as of the table row. Then we are passing the id of row and doing a ajax call to the controller to get the data. In the success of the ajax call we are adding the newly created row next to the current row(in line 37).

On click of span, if the condition check in the line 7 does not have a class named plus, that means the detail is already loaded and open. We are simply hiding the detail in line(16-17).

The controller method for getting detail is as follows-
        public JsonResult GetDetail(int id)
        {
            return Json((from c in data
                         where c.Id == id
                         select c.Detail).First(), JsonRequestBehavior.AllowGet);
        }
In the image we can see things in action. If we click the button multiple time, the ajax call will happen one time only. And on subsequent click it just show and hide the data.

Wednesday, December 1, 2010

JSON to HTML table

There was a need to converting JSON object to HTML Table. I found out different solutions over internet. But there was no generic solution where I can pass a JSON string and get the HTML table in return. So, I tried out a solution with JavaScript. The code is as follows-

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="form1">
<div id="test">
</div>
<script type="text/javascript">
var table = "[{'Country':'mycity','Name':'abc','Age':'29','Email':'test@mail.com'}," +
"{'Name':'abcd','Age':'39','Email':'test1@mail.com'}," +
"{'Name':'abcde','Age':'30','Email':'test2@mail.com'}," +
"{'Age':'30','Sex':'male','Name':'abcde'}]";
table = eval('' + table + '');
var tableObject = function (json) {
var headerCount = new Object();
var createTR = function (id) {
var tr = document.createElement("tr");
tr.ID = id;
return tr;
};
var createTH = function (html) {
var th = document.createElement("th");
th.innerHTML = html;
return th;
};
var createTD = function (html) {
var td = document.createElement("td");
td.innerHTML = html;
return td;
};
var getName = function (id) {
for (var name in headerCount) {
if (eval("headerCount." + name) == id) {
return name;
}
}
};
var pTable;
if (json.length > 0) {
var index = 0;
pTable = document.createElement("table");
var head = createTR();
for (var i = 0; i < json.length; i++)
for (var item in json[i]) {
if (!headerCount.hasOwnProperty(item)) {
head.appendChild(createTH(item));
eval('headerCount.' + item + "=" + index);
index++;
}
}
pTable.appendChild(head);
for (var i = 0; i < json.length; i++) {
var row = new createTR(i);
for (var j = 0; j < index; j++) {
var name = getName(j);
if (eval("json[" + i + "].hasOwnProperty('" + name + "')")) {
row.appendChild(createTD(eval('json[' + i + '].' + name)));
}
else
row.appendChild(createTD(''));
}
pTable.appendChild(row);
}
}
return pTable;

};
var c = new tableObject(table);
document.getElementById("test").appendChild(c);
</script>
</form>
</body>
</html>