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.

Monday, May 21, 2012

Disable past date in ajax control toolkit calendar extender

This is a simple post where we will be talking about disabling past dates in calendar extender of ajax control toolkit. Let’s take the following example-
<asp:TextBox runat="server" ID="Date1" autocomplete="off" /><br />
<ajaxToolkit:CalendarExtender ID="defaultCalendarExtender" runat="server"  TargetControlID="Date1"   />
This will display the all the dates in calendar extender. Disabling the past date is simple. We only need to set one property of the calendar extender. And this is StartDate property of the calendar extender and it is good to set the property from code behind. So, we can simply disable the past date by following code-
    protected void Page_Load(object sender, EventArgs e)
    {
        defaultCalendarExtender.StartDate = DateTime.Now;
    }


The disabled date is shown as the attached image. What if we want to change the style of the disabled date? That is also simple. The extender adds a CSS class to the disabled dates named ajax__calendar_invalid. We can check this by developer tools, for instance chrome inspector. Want we can do, right click on any disabled date and from the context menu chose Inspect Element. Then we can see the CSS class.



We can override the CSS class as per out need. For instance we can change the CSS as below-
<style>
.ajax__calendar .ajax__calendar_invalid .ajax__calendar_day 
{
    background-color:gray;
    color:White; 
    text-decoration:none; 
    cursor:default;
}
</style>
The new style looks like-

Thursday, May 17, 2012

Calculating row total, column total, grand total if a table or gridview using jQuery

In this post lets discuss a very common problem. I have answered many such posts. Finally I decided to write a blog post on it. This is regarding calculating the total of the columns, total of the rows and grand total in a table. For this case lets take textbox in each cell and on keyup we are going to do the calculation. And the code goes here-
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $(".data").keyup(function () {
                var temp, total = 0, index;
                $(this).closest("tr").find(".data").each(function (i, item) {
                    temp = parseFloat($(item).val());
                    if (!isNaN(temp))
                        total = total + temp;
                });
                $(this).closest("tr").find(".rowTotal").val(total);
                total = 0;
                index = $(this).closest("tr").find("td").index($(this).closest("td"));
                $("#tbl tr").each(function (i, item) {

                    if ($(item).find("td:eq(" + index + ") .data").hasClass("data")) {
                        temp = parseFloat($(item).find("td:eq(" + index + ") .data").val());
                        if (!isNaN(temp))
                            total = total + temp;
                    }
                });
                $("#tbl tr:last td:eq(" + index + ") .columnTotal").val(total);
                calculateGrandTotal();
            })
        });
        function calculateGrandTotal() {
            (function () {
                var temp, total = 0;
                $(".columnTotal").each(function () {
                    temp = parseFloat($(this).val());
                    if (!isNaN(temp))
                        total = total + temp;
                });
                $(".grandTotal").val(total);
            })();
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <table id="tbl">
    <tr>
        <td><asp:TextBox ID="TextBox1" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox2" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox3" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox4" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox5" CssClass="rowTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
    </tr>
        <tr>
        <td><asp:TextBox ID="TextBox6" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox7" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox8" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox9" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox10" CssClass="rowTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
    </tr>
        <tr>
        <td><asp:TextBox ID="TextBox11" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox12" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox13" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox14" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox15" CssClass="rowTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
    </tr>
        <tr>
        <td><asp:TextBox ID="TextBox16" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox17" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox18" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox19" CssClass="data" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox20" CssClass="rowTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
    </tr>
        <tr>
        <td><asp:TextBox ID="TextBox21" CssClass="columnTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox22" CssClass="columnTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox23" CssClass="columnTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox24" CssClass="columnTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
        <td><asp:TextBox ID="TextBox25" CssClass="grandTotal" ReadOnly="true" runat="server"></asp:TextBox></td>
    </tr>
    </table>
    </form>
    </body>
</html>

Getting started: debugging with Safari 5

Today I was exploring the web debugging option with safari 5. Its really cool. I wish to put down the basic of how to start with debugging features.

Lunching the debugger tool:
First download Safari 5 from here. Click on the settings icon to the right top corner. A drill down menu will come up. From the menu chose the Preferences option. This will open a popup. From the popup check the checkbox "Show develop menu in menu bar" if its uncheck. On doing this there will be an additional option in the toolbar named "Develop". Thats all. From the developer menu we can chose the option we want to use out of which "Show web inspector" is the most common one.


Even we can lunch the web inspector by right clicking any element and this will show a context menu. From the context menu we can choose "Inspect element".

We can also check various option given by Safari 5 developer tool here.

Tuesday, May 15, 2012

How to find javascript files, css files and images from a web page

We can find many examples for various plugins. Now how to extract the code used in that case. Let me explain this with an example. As an example I wish to take the following page-

http://www.metamorphozis.com/free_templates/free_css_templates/metamorph_carousel/gallery.html.

Now to get the source code first thing that we can do is view source the page and find the desired source code needed. Finding the desired source code comes with practice. One instance is the you know why you have visited some pages and what could be the desired HTML for your case. For example in this case I just need the sideshow. So I will be looking for the images and the corresponding script, CSS files. I don't need the header and footer. In my case div with id content is the desired HTML I am looking for. And I need the following script and CSS files-
<link href="styles.css" rel="stylesheet" type="text/css" />
<!-- Pirobox setup and styles -->
<script type="text/javascript" src="lib/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="lib/pirobox.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        $().piroBox({
            my_speed: 400, //animation speed
            bg_alpha: 0.1, //background opacity
            slideShow: false, // true == slideshow on, false == slideshow off
            slideSpeed: 4, //slideshow duration in seconds(3 to 6 Recommended)
            close_all: '.piro_close,.piro_overlay'// add class .piro_overlay(with comma)if you want overlay click close piroBox
        });
    });
</script>
<link href="images/style.css" rel="stylesheet" type="text/css" />
Now few things are still pending. If I save the file in my local system and try to execute, it will not work. Reasons are-
  1. Image are missing in the local drive. As you can see the images are relative path starting with images/....
  2. CSS files are relative
  3. Script files are relative
Now easy and quick way to resolve the problem of the file location is the use of browser debugging tools.

Using IE developer tool:
If you are using IE 9 onward you can use the IE 9 debugger tool to find the file locations. Open the page in IE 9, click F12, debugger tool will popup. Go to network tab, click Start Capturing button and refresh the same page again. Below the network tab you will find the actual location of all the files that are getting loaded with the page. You can right click the link and copy and use the full path.


Using Firebug:
Go to firebug tool, select the Net option, enable the net option if its disabled, refresh the page. You can find the set of URLs displaying in the net tab. In firebug there are classification of the different files loaded with the page like images, flash, CSS, js and so on. You can right click the link and copy and use the full path.


Using Safari, Opera and Google Chrome developer tool:
In all these browse, open the page. Right click on any place in the page, and from the context menu chose "Inspect Element". This will popup the developer tool. Then go to Network tab in the developer tool and refresh the page. You can see all the CSS, images and scripts loaded.

Monday, May 14, 2012

Conditional if in attribute in a HTML element using razor syntax.

This is little interesting. While on asp.net forum, I came across a requirement of conditional if in deciding css class of a HTML tag. I thought of writing this as a blog post.

Lets take a small section of the following code-
@for (int i = 0; i < 10; i++)
{
    if (i%2 == 0)
    {
    <div class="a">
        Test
    </div>
    }
    else
    {
    <div class="b">
        Test
    </div>
    }
}
 
In the above code segment we are simply checking the odd and even and setting the css class as "a" or "b". But the code is not optimal. We can reduce the lines of code by the following-
@for (int i = 0; i < 10; i++)
{
    <div class="@(i % 2 == 0? 'a': 'b')">
        Test
    </div>
}

Sunday, May 13, 2012

Change asp.net checkbox text using checkbox id in JavaScript

Lets first check how a check box is getting rendered in asp.net page. Lets have the following HTML-
<asp:CheckBox ID="chkAll" runat="server" Text="All" />
The above control is getting rendered like-
<input id="ContentPlaceHolder1_chkAll" type="checkbox" name="ctl00$ContentPlaceHolder1$chkAll" />
<label for="ContentPlaceHolder1_chkAll">All</label>
Now to change the text for the check box is nothing but changing the innerHTML of the rendered label. We can do this change by-
<script type="text/javascript">
    function changeLabel(checkboxID, text) {
    debugger;
        var allLabel = document.getElementsByTagName("label");
        for (i = 0; i < allLabel.length; i++) {
            
            if (allLabel[i].htmlFor == checkboxID) {
                allLabel[i].innerHTML = text;
                break;
            }
        }
    }
    changeLabel('<%=chkAll.ClientID %>', "new text");
</script> 
Using jQuery it much more simple-
$("label[for=<%=chkAll.ClientID %>]").html(new text);

Wednesday, May 2, 2012

jQuery datepicker problem with control generated in loop in asp.net MVC

Today I have faced a unique problem with datepicker while working in asp.net MVC.

I have added some textboxes using HTML.TextBoxFor inside a loop and then implemented datepicker on the textboxes. Part of the code goes like this-
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.js" type="text/javascript"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.9/jquery-ui.js" type="text/javascript"></script>
<link href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.9/themes/redmond/jquery-ui.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
    $(document).ready(function () {
        $("input[id*=DateTaken]").datepicker({
            dateFormat: "dd/mm/yy",
            changeMonth: true,
            changeYear: true
        });
    });
</script>

@for (int i = 0; i < 10; i++)
{
    @Html.TextBoxFor(m=> m.EventDate)
}
Now problem is that datepicker is enabled in all the textboxes, but on selection of date from the picker its adding the selected date to the first text box only. On digging further I can see the textboxes HTML got generated like below-
<input id="DateTaken" name="EventDate" type="text" value="" />
<input id="DateTaken" name="EventDate" type="text" value="" />
<input id="DateTaken" name="EventDate" type="text" value="" />
.
.
.
That is control got generated with same id and name 10 times. This is not a valid HTML. But suppose we need the control to be generated like this. Such control collection will be posted as an array with the name EventDate to the controller.

This can be solved very easily. Just generating different ID for each input control. That is the following code change will solve the problem-
@Html.TextBoxFor(m=> m.EventDate, new { @id = "DateTaken"+item.ToString() })