Showing posts with label jQuery UI dialog box. Show all posts
Showing posts with label jQuery UI dialog box. Show all posts

Monday, February 6, 2012

Showing ASP.NET MVC 3.0 Views in jQuery ui dialog & Posting MVC forms from jQuery ui dialog

“I blogged on this topic in September 2010 and received a few mail about showing how to add new entities (as opposed to update) and also an implementation of this in MVC 3.0 using Razor. While the concepts and explanation in the previous blogs still hold correct, here’s an implementation using MVC 3.0 and Razor with some improvements. My apologies for not posting this blog as sooner; also really appreciate your comments & mails.”
The requirement I was given was, to show the form to Add/Edit Products and the details view, in a jQuery UI dialog. Also these dialogs were to be shown at many places all across the application. I certainly did not want to render views by default, everywhere the dialog was to be shown. So I created extenders which would have scripts to load the view when needed.
To start with, the scripts that we would need to have in the layout would be jQuery and jQuery UI with the style sheet. Additional scripts will be needed for Ajax form posting but We’ll be including them in the form itself.
Here’s some basic infrastructure -
The Product class-
public class Product {
    
    public int Id { get; set; }
    
    [Required]
    public string Name { get; set; }
    
    [Required]
    public string Description { get; set; }

}
And a product’s service for CRUD operations
public class ProductService {


    private readonly string ProductsInSession = "ProductsInSession";

    /// <summary>
    /// Some Dummy products
    /// </summary>
    private List<Product> _initialData {
        get {
            return new List<Product> {

                new Product() { 
                        Id = 1, 
                        Name = "My New Product", 
                        Description = "This is a cool way to extend the jQuery UI dialog!" },

                new Product() { 
                        Id = 2,
                        Name = "My New B", 
                        Description = "Details for Product Beee!" },

                new Product() { 
                        Id = 3,
                        Name = "My New C", 
                        Description = "Details for Product Ceee!" },

                new Product() { 
                        Id = 4,
                        Name = "My New D", 
                        Description = "Details for Product Deee!" },

                new Product() { 
                        Id = 5,
                        Name = "My New E", 
                        Description = "Details for Product Eeee!" }
            };
        }
    }
    
    /// <summary>
    /// Get and Set List of products from and to the session
    /// </summary>
    private List<Product> _Products {
        get {
            List<Product> data = null;
            if (HttpContext.Current.Session[ProductsInSession] == null) {
                data = _initialData;
                HttpContext.Current.Session[ProductsInSession] = data;
            }
            else {
                data = HttpContext.Current.Session[ProductsInSession] as List<Product>;
            }
            return data;
        }
        set {
            HttpContext.Current.Session[ProductsInSession] = value;
        }
    }


    public List<Product> GetProducts() {
        return _Products;
    }


    public Product GetProduct(int id) {
        return _Products.Single(p => p.Id == id);
    }


    public void AddUpdateProduct(Product product) {
        // Could definitely do better that this but the demo is about the dialog
        var products = _Products;
        if (product.Id == 0) {
            product.Id = _Products.Max(p => p.Id) + 1;
            products.Add(product);
        }
        else {
            products.Single(p => p.Id == product.Id).Name = product.Name;
            products.Single(p => p.Id == product.Id).Description = product.Description;
        }
        //Save back to session
        _Products = products;
    }

}
Displaying Non-Form views -
Here’s the Action method from the ProductController -
public ActionResult Details(int id) {

    return View(_productService.GetProduct(id));
}
The View -
@model DialogExtender_MVC3.Models.Product

<fieldset>
    <legend>Product</legend>

    <div class="display-label">Name</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-label">Description</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Description)
    </div>
</fieldset>
The and Extender with the script to load the data (I’m calling it ProductDetailsDialog and putting it in the shared folder)
<div id="ProductDetailsDialogDiv" title="Product details" style="display: none;">
</div>

<script type="text/javascript">

    function openProductDetailsDialog(id) {

        $.ajax({
            type: "GET",
            url: encodeURI('@Url.Action("Details", "Product")' + "?id=" + id),
            cache: false,
            dataType: 'html',
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                $("#ProductDetailsDialogDiv").html(errorThrown);
            },
            success: function (data, textStatus, XMLHttpRequest) {
                $("#ProductDetailsDialogDiv").html(data);
            },
            complete: function (XMLHttpRequest, textStatus) {

                $('#ProductDetailsDialogDiv').dialog({
                    modal: true,
                    width: "300px",
                    close: function (event, ui) { $("#ProductDetailsDialogDiv").html(""); },
                    buttons: {
                        "Ok": function () { $(this).dialog("close"); }
                    }
                });
            }
        });
    }

</script>
The openProductDetailsDialog received the product Id and calls the Details action and load whet ever it gets in the DIV ProductDetailsDialogDiv. and finally show the DIV in a dialog. The DIV is cleared when the dialog is closed. It’s always a good practice to use $.ajax (as opposed to $.get() or $.load()) as .ajax gives us handlers for error enabling better control for debugging and maintenance.
And here’s how we invoke it  - just place the two lines of markup in any view in the application -
<!--The link that will show the dialog-->
<a href="javascript:openProductDetailsDialog(1);">Product detail</a>
<!--The Dialog extenders will sit here quietly unless asked to do something-->
@Html.Partial("ProductDetailsDialog")

Displaying Form views -
To the same trick for forms we would have to take care of showing the validations in the dialog itself.
The action should send back the form if any validations have failed and something to notify that the post is successful if all the validations passed.
Here’s that something – I created a one line View Called AddEditSuccess.cshtml that looks like -
"E-d-i-t---S-u-c-c-e-s-s-f-u-l"
and placed it in the shared folder.
I created a ProductViewModel class with one member – Product. In a practical scenario this view model will have a lot more stuff than what is has now.
public class ProductViewModel {

    public Product Product { get; set; }
}
Here’s the AddEditAction from ProductController -
public ActionResult AddEdit(int? id) {

    if (id.HasValue && id.Value != 0) {
        //Edit an existing Product
        return View(new ProductViewModel { Product = _productService.GetProduct(id.Value) });
    }
    else {
        //Add a new Product
        return View(new ProductViewModel { Product = new Product { Id = 0 } });
    }
}


[HttpPost]
public ActionResult AddEdit(ProductViewModel vm) {

    if (ModelState.IsValid) {

        _productService.AddUpdateProduct(vm.Product);
        return PartialView("AddEditSuccess");
    }

    return View(vm);
}
Note that we identify whether it is a add or edit based on the availability of the primary key. A similar logic sits in the Product service too which is why the same action and service method is used to achieve both adding and editing products.
Here’s the AddEdit view
@model DialogExtender_MVC3.Models.ProductViewModel

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Ajax.BeginForm(new AjaxOptions { OnComplete = "addEditProductComplete" })) {
    
    @Html.ValidationSummary(true)
    
    <fieldset>
        <legend>Product</legend>

        @Html.HiddenFor(model => model.Product.Id)

        <div class="editor-label">
            @Html.LabelFor(model => model.Product.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Product.Name)
            @Html.ValidationMessageFor(model => model.Product.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Product.Description)
        </div>
        <div class="editor-field">
            @Html.TextAreaFor(model => model.Product.Description)
            @Html.ValidationMessageFor(model => model.Product.Description)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>

    </fieldset>
}
For the Ajax options and client validations to work we need the scripts – jquery.unobtrusive-ajax, jquery.validate, jquery.validate.unobtrusive. The Ajax options in the form will call the JavaScript method – addEditProductComplete after the response from the form post is received. This method checks if the form was successfully posted or not. If not it keep the dialog in place and updated the contents of the dialog.
I’m calling this ProductAddEditDialog and keeping it in the shared folder. For naming the dialog extenders I’m using the format <ObjectTypeName><ActionName>Dialog just to keep things consistent. You can choose your own naming conventions. Here’s the ProductAddEditDialog.cshtml -
<div id="ProductEditDialogDiv" title="Product" style="display: none;">
</div>

<script type="text/javascript">

    function openProductEditDialog(id) {

        $("#ProductDetailsDialogDiv").html("");

        $.ajax({
            type: "GET",
            url: encodeURI('@Url.Action("AddEdit", "Product")' + "?id=" + id),
            cache: false,
            dataType: 'html',
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                $("#ProductEditDialogDiv").html(XMLHttpRequest.responseText);
            },
            success: function (data, textStatus, XMLHttpRequest) {
                $("#ProductEditDialogDiv").html(data);
            },
            complete: function (XMLHttpRequest, textStatus) {
                
                $('#ProductEditDialogDiv').dialog({
                    width: '500px',
                    modal: true
                });
            }
        });
    }

    function addEditProductComplete(xmlHttpRequest, textStatus) {
        
        var data = xmlHttpRequest.responseText;

        if (data.indexOf("E-d-i-t---S-u-c-c-e-s-s-f-u-l", 0) >= 0) {
            $("#ProductDetailsDialogDiv").html("");
            $('#ProductEditDialogDiv').dialog('close');
            //  call reload / refresh or any other post edit action.
        }
        else {
            $('#ProductEditDialogDiv').html(data);
            $("#EditProductButtons").hide();
        }
    }
</script>
And just like the non-form dialogs, here’s the two lines of markup that will show your dialog anywhere you want -
<a href="javascript:openProductEditDialog(1);">Edit Product</a>
@Html.Partial("ProductAddEditDialog")
To provide  the link to add, all we need is to pass a 0 or null or blank to the openProductEditDialog method -
<a href="javascript:openProductEditDialog('');">Add Product</a>
@Html.Partial("ProductAddEditDialog")
A Demo Solution for the post can be downloaded here.
Any comments/suggestions welcomed.

Sunday, September 12, 2010

jQuery dialog extender for Asp.Net MVC Views

“This post and the demo solution is still valid for MVC 2.o.

However, a new version of this post Using MVC 3.0 and Razor with minor improvements is available here.”

This idea came to me during a discussion at work. The requirement was to create put an Add/Edit form in a dialog box. I’ve always been against the idea of rendering the markup inside the dialog div before hand, as the user should NOT pay the cost of a rendering something that might not be needed.

The idea went further to being able to provide the link to edit dialog any where in the application. This triggered the thought of the extender pattern that we use to ajaxify server controls in the web forms. So here’s the implementation, create a view then create a dialog extender for the view. The first example will be a regular view (without a form), the second (in the next blog) will be an edit form.

Extending a simple view (without a form in it):

As always, the application starts with a model:

    public class Product {

        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
   }

Here’s a controller (with dummy data) for it:

    public class ProductsController : Controller {

        private List<Product> _data = new List<Product> {

                        new Product() { 
                                Id = new Guid("4C341BA3-E971-43C0-8BB1-07F51320E10B"), 
                                Name = "My New Product", 
                                Description = "This is a cool way to extend the jQuery UI dialog!" },

                        new Product() { 
                                Id = new Guid("D12C7542-99D0-4610-AB47-7A38F45CD026"), 
                                Name = "My New B", 
                                Description = "BBBeeeeeeeeeeeeeeeeeeeeeeeeeee!" },

                        new Product() { 
                                Id = new Guid("66B1F0EA-1230-4D62-9BE1-A5158BE10784"), 
                                Name = "My New C", 
                                Description = "CCCeeeeeeeeeeeeeeeeeeeeeeeeeee!" },

                        new Product() { 
                                Id = new Guid("959FB827-BBEE-47D8-9B4D-14BF3092A1F1"), 
                                Name = "My New D", 
                                Description = "DDDeeeeeeeeeeeeeeeeeeeeeeeeeee!" },

                        new Product() { 
                                Id = new Guid("C093608F-E252-4758-9924-62B2955C0AC5"), 
                                Name = "My New E", 
                                Desctiption = "EEEeeeeeeeeeeeeeeeeeeeeeeeeeee!" }
        };

        public ActionResult Details(Guid id) {

            var model = _data.SingleOrDefault(p => p.Id == id);
            return View(model);
        }

        public ActionResult Index() {

            return View();
        }
    }

And here’s the details view (Details.ascx):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCDialogExtender.Models.Product>" %>
<fieldset>
    <legend>Fields</legend>
        
    <div class="display-label">Name</div>
    <div class="display-field"><%: Model.Name %></div>
        
    <div class="display-label">Description</div>
    <div class="display-field"><%: Model.Description %></div>
        
</fieldset>

The details view can be a view or a partial view. Here’s the extender to the view (ProductDetailsDialog.ascx). I will add this view in the Shared folder so that it can be accessed from any view across the application. Also, since this is going in the Shared folder, I will follow the naming convention - “<Controller name><Action Name>Dialog” .

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCDialogExtender.Models.DialogExtender>" %>

<div id="ProductDetailsDialogDiv" title="Product details" style="display:none;"></div>

<script type="text/javascript">

    $(document).ready(function () {
        $('#ProductDetailsDialogDiv').dialog({
            autoOpen: false,
            modal: true,
            close: function (event, ui) { $("#ProductDetailsDialogDiv").html(""); }
        });
    });
        $.ajax({
            type: "GET",
            url: encodeURI('<%= Url.Action("Details", "Products") %>' + "?id=" + id),
            cache: false,
            dataType: 'html',
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                $("#ProductDetailsDialogDiv").html(errorThrown); 
            },
            success: function (data, textStatus, XMLHttpRequest) {
                $("#ProductDetailsDialogDiv").html(data); 
            },
            complete: function (XMLHttpRequest, textStatus) {
                $("#ProductDetailsCloseButton").click(function () { $('#ProductDetailsDialogDiv').dialog("close"); });
                $('#ProductDetailsDialogDiv').dialog("open"); 
            }
        });
    }

</script>

As soon in the view above, the dialog container DIV tag is emply an will be populated only when the user demands it. The javascript method responsible for populating and opening the dialog box will be called from outside this view so it will be handy to have a consistent naming convention, in my example I’ve chosen - “open<View Name>”. Again, since multiple such dialog may be present on the page I will follow the naming convention of “<View Name>Div” to avoid duplicate Id issue.

The detail extender inherits from a view model called DialogExtender which is an empty class right now but later if something needs to be added to the dialogs at a global level, the DialogExtender will be the way to go.

Finally here’s how we place a link to see the dialog extender:

    <p>
        <!--The link that will show the dialog-->
        <a href="javascript:openProductDetailsDialog('4C341BA3-E971-43C0-8BB1-07F51320E10B');">Product details</a>
        <!--The Dialog extender will sit here quietly-->
        <% Html.RenderPartial("ProductDetailsDialog", new DialogExtender()); %>
    </p>

OR in a list view

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MVCDialogExtender.Models.Product>>" %>

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

    <h2>List</h2>

    <table>
        <tr>
            <th></th>
            <th>
                Name
            </th>
        </tr>

    <% foreach (var item in Model) { %>
        <tr>
            <td>
                <a href="javascript:openProductDetailsDialog('<%: item.Id %>');">Details</a>
            </td>
            <td>
                <%: item.Name %>
            </td>
        </tr>    
    <% } %>

    </table>

    <% Html.RenderPartial("ProductDetailsDialog", new DialogExtender()); %>

</asp:Content>

The results:

Link

List

 

The source code of this demo can be downloaded here.

With the intention to keep the blogs short I will be extending MVC forms in the next blog.

 

Thoughts, opinions, ideas and comments welcomed.

Saturday, August 7, 2010

The jQuery formula - Blocking UI and showing progress dialog across Ajax calls with Asp.Net MVC

This post in in continuation to my previous post where I create a progress dialog blocking the UI across asynchronous postbacks for WebForms. I will do the same thing here using Asp.Net MVC, except for the fact that there no such thing as postback in MVC, It’s all Get - Post -Put – Delete (a true web framework) . Much of the initial content will be common not identical.

The goal here is to not allow user to interact with the application, at the same time showing the user a dialog saying “Wait, let me first finish what you just asked me to do!”

The version of jQuery that I’m using is 1.4.1 (downloadable here) and the version of jQuery UI I’m using is 1.8.2(downloadable here). I’ll be using ASP.NET MVC 2.0 but this demo is valid for any web framework which allows making Ajax calls using jQuery.

The source code for this demo can be downloaded here.

Plugging in jQuery UI in to a new ASP.NET web application:

Well start with creating a new web application using Visual Studio 2010:

The application template by default contains the jQuery scripts in the /Scripts folder (I deleted the files non-relevant to this demo to minimize solution size).  jQueryUI scripts and CSS is required to be downloaded and include it in the solution via the following entries in the master page:

    <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>    
    <script src="../../Scripts/jquery-ui-1.8.2.custom.min.js" type="text/javascript"></script>
    <link href="../../Content/jQueryUI/redmond/jquery-ui-1.8.2.custom.css" rel="stylesheet" type="text/css" />

Creating a jQuery UI dialog with the content to show

We need to create two methods one to open the jQuery dialog and the other to close it. With the intention to keep all related javascript in one place and out of the .aspx pages, I created a new JScript file called jqueryProgressDialog.js, placed it in the Scripts folder and create two methods in it:

function showProgressDialog() {

    $("#progressDialog").dialog({
        autoOpen: false,
        modal: true,
        bgiframe: true
    });

    $('#progressDialog').dialog('open');

}

function hideProgressDialog() {

    if ($('#progressDialog').dialog('isOpen')) {

        $('#progressDialog').dialog('close');
    }
}

And include this script file in our master page -

    <script src="../../Scripts/jqueryProgressDialog.js" type="text/javascript"></script>

What will be shown in the dialog box will be defines using a DIV tag with id = “progressDialog”. We will put this in the Master Page so that it is available to the while application.

    <div id="progressDialog" title="Welcome to my demo!" style="display: none;">
        <img src="../../Images/AjaxProgress.gif" alt="Processing"/>
        <p>
            Please wait while your request is processed.
        </p>
    </div>

Obviously this solution will not work for the pages that do not  inherit from this master page.

 

Calling the progress dialog while asynchronous request

To consume this infrastructure, we need to add specify out show and hide dialog functions to the start and end of a request. You can either surpass the framework’s Ajax helpers and go the jquery way making Ajax calls -

The View -

    <p>
        <input type="button" value="Click here to for a 2 seconds long asynch call" onclick="makeAnAjaxCall();" />
    </p>

    <script type="text/javascript">

        function makeAnAjaxCall() {

            $.ajax({
                type: "GET",
                url: '<%= Url.Action("SleepFor2Seconds", "Home") %>',
                data: "{}",
                cache: false,
                beforeSend: showProgressDialog,
                complete: hideProgressDialog
            });
        }
    
    </script>

The Controller:

        public bool SleepFor2Seconds()
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
            return true;
        }

Or go with the framework infrastructure for secure posting your forms.

The View -

    <div>
        <% using (Ajax.BeginForm(new AjaxOptions()
                                 {
                                     HttpMethod = "post",
                                     OnBegin = "showProgressDialog",
                                     OnComplete = "hideProgressDialog"
                                 }
                     ))
           {%>
        <fieldset>
            <legend>Some form</legend>
            <p>
                FirstName:
                <%= Html.TextBox("FirstName", "FirstName")%>
            </p>
            <p>
                LastName:
                <%= Html.TextBox("LastName", "LasName")%>
            </p>
            <%= Html.AntiForgeryToken()%>
            <p>
                <input type="submit" value="Click here to Asynchronously post this form" />
            </p>
        </fieldset>
        <% } %>
    </div>

NOTE: For the Ajax.BeginForm to work the helper script files MicrosoftAjax.js and MicrosoftMvcAjax.js are required.

    <script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
    <script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>

The Controller actions:

        [HttpGet]
        public ActionResult Index()
        {
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Index(FormCollection formcollection)
        {
            Thread.Sleep(TimeSpan.FromSeconds(1));
            return View();
        }

In either case, here’s the result:

jQueryProgressDialog_MVC

Friday, August 6, 2010

The jQuery formula - Blocking UI and showing progress dialog across asynchronous postbacks with ASP.NET Web Forms.

The goal here is to not allow user to interact with the application, at the same time showing the user a dialog saying “Wait, let me first finish what you just asked me to do!” The version of jQuery that I’m using is 1.4.1 (downloadable here) and the version of jQuery UI I’m using is 1.8.2(downloadable here). I’ll be using ASP.NET 4.0 Web forms but this demo is valid for pervious version with compatible ASP.NET AJAX (AKA Microsoft Ajax) version available.

The source code for this demo can be downloaded here.

Plugging in jQuery UI in to a new ASP.NET web application:

Well start with creating a new web application using Visual Studio 2010:

The application template by default contains the jQuery scripts in the /Scripts folder (I deleted the files non-relevant to this demo to minimize solution size).  jQueryUI scripts and CSS is required to be downloaded and include it in the solution via the following entries in the master page:

    <link href="Styles/jQueryUI/redmond/jquery-ui-1.8.2.custom.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery-ui-1.8.2.custom.min.js" type="text/javascript"></script>

Creating a jQuery UI dialog with the content to show

We need to create two methods one to open the jQuery dialog and the other to close it. With the intention to keep all related javascript in one place and out of the .aspx pages, I created a new JScript file called jqueryProgressDialog.js, placed it in the Scripts folder and create two methods in it:

function showProgressDialog() {

    $("#progressDialog").dialog({
        autoOpen: false,
        modal: true,
        bgiframe: true
    });

    $('#progressDialog').dialog('open');

}

function hideProgressDialog() {

    if ($('#progressDialog').dialog('isOpen')) {

        $('#progressDialog').dialog('close');
    }
}

And include this script file in our master page -

    <script src="Scripts/jqueryProgressDialog.js" type="text/javascript"></script>

What will be shown in the dialog box will be defines using a DIV tag with id = “progressDialog”. We will put this in the Master Page so that it is available to the while application.

    <div id="progressDialog" title="Welcome to my demo!" style="display: none;">
        <img src="Images/AjaxProgress.gif" alt="Processing"/>
        <p>
            Please wait while your request is processed.
        </p>
    </div>

Obviously this solution will not work for the pages that do not  inherit from this master page.

 

Calling the progress dialog while asynchronous request

To complete the infrastructure, we need to add an instance of  ScriptManager to the master page.

    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>

and use the methods exposed by PageRequestManager to specify the client side javascript methods that will be executed with the request start and end.

The PageRequestManager is a client side class that exposes events and functions to manipulate the asynchronous post back using the Script Manager. The intelessence support will be available if an instance of Scriptmanager or ScriptManagerProxy is available on the page. The methods exposed by the PageRequestManager are worth exploring if you are working extensively with Asp.Net Ajax.

PageRequestManager

To hook-up the progress dialog start stop function, we will add the following lines of script to our jqueryProgressDialog.js file (so that all the javascript code related to the dialog is at one place):

$(document).ready(function () {

    Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(showProgressDialog);
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(hideProgressDialog);
});

With that the infrastructure is complete, now we need to see it work!

And to do that I will create an update panel on the home page and add a simple button in it to invoke a postback.

    <asp:UpdatePanel ID="UpdatePanel1" runat="server" ChildrenAsTriggers="true" UpdateMode="Always">
        <ContentTemplate>
            <p>
                <asp:Button ID="Button1" runat="server" Text="Click here to for a 2 second asynch postback"
                    OnClick="Button1_Click" />
            </p>
        </ContentTemplate>
    </asp:UpdatePanel>

Code behind -

        protected void Button1_Click(object sender, EventArgs e)
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
        }

And guess what showed up -

jQueryProgressDialog

 

After introduced to the ASP.NET MVC framework, it has been the framework of choice to me. So if you do not like server side ajax or (like me) prefer MVC over web forms –here’s the same trick with the MVC framework!

Friday, June 26, 2009

jQuery UI Dialog in jQuery Grid (jqGrid) with ASP.NET MVC

This post is in continuation with my previous post where we created an ASP.NET MVC with JQgrid. Also, as we will be using the same solution on which we worked in the last post. Versions used –

  • jQuery – 1.3.2
  • jqGrid – jqGrid 3.5 ALFA 3.
  • jQuery UI 1.7.1

The source code of the demonstration can be downloaded here.

A jQuery Dialog box is just HTML markup tags that sit in DIV tag inside your page. This DIV tag is made invisible to the user when the user is on the page by style="display:none" attribute. This is done obviously because the user need to see this content in a dialog box only when it is invoked. Let’s start with including jQuery script files to the solution. It can be downloaded from here. I’ve added the jQuery UI scripts in a subfolder called “jQueryUI” within the Scripts folder. Now open the solution and go to Index.aspx where we have the jqGrid. On this page copy paste the following HTML tags in the MainContent place holder (it is not important where you put this tag on the page):

<div id="detailsDialog" style="display:none" title="Movie Details">    
    <table>
        <tr>
            <td><h2 id="movieName"></h2></td>
            <td>
                <img id="movieImage" src="" alt=""/>
            </td>
        </tr>
        <tr>
            <td>Director</td>
            <td id="director"></td>
        </tr>
        <tr>
            <td>Release Date</td>
            <td id="releaseDate"></td>
        </tr>
        <tr>
            <td>IMDB User Rating</td>
            <td>
                <a id="iMDBUserRating" target="_blank" href=""></a>
            </td>
        </tr>
        <tr>
            <td>Plot</td>
            <td id="plot"></td>
        </tr>
    </table>
    </div>
This is the set of tags that will be run by the Dialog box. Also for that we need to import the following scripts on the page: 
<script src="<%= this.ResolveClientUrl("~/Scripts/jQueryUI/ui.core.js") %>" type="text/javascript"></script>
<script src="<%= this.ResolveClientUrl("~/Scripts/jQueryUI/ui.draggable.js") %>" type="text/javascript"></script>
<script src="<%= this.ResolveClientUrl("~/Scripts/jQueryUI/ui.resizable.js") %>" type="text/javascript"></script>
<script src="<%= this.ResolveClientUrl("~/Scripts/jQueryUI/ui.dialog.js") %>" type="text/javascript"></script>
Now we have the tags ready, we need some jQuery to dynamically put data in there. The jqGrid that we are using has some events associated to it like “onSortCol”, “ondblClickRow” etc. For details on these events please refer to the documentation here. The event that we will use here will be onSelectRow. As the name suggests the event will be fired when the user clicks on a row and we will create a handler for the event. Let the name of the handler be – ShowMovieDetails. The Event with the handler needs to be added to the jqGrid’s properties:
onSelectRow: ShowMovieDetails 
Please note that ShowMovieDetails is not proceeded by the parenthesis “()”. Doing so will actually execute the function. In order to assign but not execute we drop the parenthesis. This event, when fired, sends the value of the selected row number to the handler as a parameter. In our scenario the handler needs to do the following this: 1. Create a dialog box, 2. Set the values dynamically in the DIV based on the row selected. 3. Show the dialog box. To achieve this we will use a method in jqGrid to get the cell value of the grid. This method is – getCell(rowId, columnId). This can be invoked as - $(<jqGridSelector>).getCell(rowId, colId). Here’s the handler for our purpose:
function ShowMovieDetails(rowid) {

    $('#detailsDialog').dialog({
        autoOpen: false,
        width: 550,
        height: 400,
        modal: true
    });

    $("#movieName").text($("#list").getCell(rowid, 1));
    
    $("#movieImage").attr("src", $("#list").getCell(rowid, 6));
    $("#movieImage").attr("alt", $("#list").getCell(rowid, 1));
    
    $("#director").text($("#list").getCell(rowid, 2));

    $("#releaseDate").text($("#list").getCell(rowid, 3));

    $("#iMDBUserRating").text($("#list").getCell(rowid, 4));
    $("#iMDBUserRating").attr("href", "http://www.imdb.com/find?s=tt&q=" + $("#list").getCell(rowid, 1))

    $("#plot").text($("#list").getCell(rowid, 5));

    $('#detailsDialog').dialog('open');
}
Please take note of the properties in the dialog. If you do not use the dialog('open') method separately to open the dialog box, the dialog box will open but only for the first time. Subsequent selection of rows will have no response. To mitigate this, we declared the dialog with autoOpen as false and open the dialog box separately using dialog('open'). Now go ahead and run the application for gratification. This is what shows up: There many methods attached to the Dialog Box. We can make this as an editable form of the data in the grid row and have buttons on the dialog box making Ajax call to execute CRUD operation. Also, pagination can be added to it in order to move from one row details to the next or previous one. That will actually complete the DetailsView data control using jQuery. You may explore the exposed methods here.