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 create new object (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.

Tuesday, August 9, 2011

Slides and solution from MVC 3.0 presentation Session 2 at SDC Meetup Chicago

It was a privilege to present the second final session for ASP.NET MVC 3.0 @ SDC meetup, covering the design considerations around MVC.

Many thanks to the event hosts and as always, the audience interaction in the end was the best part.

 

Following are the slides and solution from the presentation -

Download slides here – | – Download the demo solution here.

 

Here are some if the links that can up in the presentation during discussion -

Also, I strongly recommend taking a look at the awesome open source project called “ASP.NET MVC Project Awesome” - http://awesome.codeplex.com/ for great jQuery Ajax helpers (controls).

 

Feel free to mail/post me any question that you might have.

Thursday, July 14, 2011

Slides and solution from MVC 3.0 presentation Session 1 at Software Development Community (SDC) Chicago

It was a great pleasure to present MVC 3.0 at Software Development Community (SDC) Chicago July 10th 2011.

Thank to Joel Shaw, Michael Kappel and the organizing team.

The audience interaction was great. I will try to accommodate demo for the previous session’s question in my the next session on August 7th.

 

Following are the slides and solution from the presentation -

Download slides here – | – Download the demo solution here.

 

Looking forward to the August workshop.

Monday, May 2, 2011

Introduction to ASP.NET MVC 3 @ Chicago .NET User Group

 

It was a privilege to present at CNUG, thanks to the great audience, Keith Franklin and the organizing team.

You can download the slides and the demo solution from my presentation.

 

If you have any queries about the presentation / demo solution, feel free to send a mail or post a comment. I will get back as the earliest.

Friday, December 31, 2010

The Beez Neez of 2010

Here’s the list of things that “really” caught my attention in 2010. By really I mean, not just caught my attention but made me spend time in it and go – “That is so cool!”
I finally rated these developments of 2010 base on  #1 if they are entirely new (or how great are the 2010 features), #2 how much time I spent on it and #3 with how many people I went – “Hey did you hear about ….”

 

IE9IE 9

Somewhere between IE 6 & so called, Microsoft's complacency, Internet Explorer lost market to competitors. But the very promising  Internet Explorer 9 looks like a great comeback. So much that, I think, IE9 might have a place in this list for 2011. If you have been following Ray Bango, you would get a lot of information first hand. The Beta version is slicker, smarter, flashier has some great tools. Big features include the support for HTML5 & CSS3 and Chakra, the new JavaScript engine.

WindowsAzure

“All In” Cloud services

With the release of Windows Azure and SQL Azure cloud Platform as a Service (PaaS), this year Microsoft went “All in!” the cloud.  The cloud version of SQL server made Azure services an easier sell and the possibility of having composite solution (computing and/or storage) addresses some of the skeptics. Some of more news came in with the new and improved BPOS rebranded as Office 365 and Windows Azure platform appliances allowing partners and large enterprises to run Windows Azure on premise.

Don’t miss to claim your free trial while it is available.

 

Samsung_FocusWindows Phone 7 (and the worthy mention – HTC Evo)

For all those who kept going “Are they there yet?” - well yes, it’s finally here and it looks pretty good. I got my hands on Samsung focus and I liked it.

Microsoft’s touch screen based smart phones did come in late but I admire the confidence of some folks at Microsoft when they say, “2010 is not the end of the world.” The statement has some substance in it but how much, only time will tell.

I want to mention the Android based HTC Evo, I used that for 2 weeks and it too was very impresses.

Using these two phone made me believe that the iPhone’s era of dominance is definitely now over.

 

NuGet

NuGet (initially released as NuPack)

NuGet  is a free open source developer focused package manager intent on simplifying the process of incorporating third party libraries into a .NET application during development. It was the big surprise gift that came to me from Microsoft camp this year to the developers who love open source. Within weeks of it’s release, I could see people in open source community forums asking – “is there a NuPack for this?”

 

Visual studio 2010VisualStudio-Twitter-04

Needs no introduction, our favorite IDE just got more efficient, extendable and productive. It carries the cloud, the NuPack, the phone and the browser, the web , the desktop and is now loaded with power tools.

Microsoft continues to supply the Express versions of Visual Studio for free.

 

and for my top pick, with drum rolls…

 

MVC3Razor sharp MVC 3

It’s the same MVC framework that brought us closer to the Web, now with the new Razor View engine making your view look more elegant than ever. It is now powered with Chart helper, Unobtrusive Ajax, client validation with deeper extensibility.

The current installer installs MVC 3 side-by-side with ASP.NET MVC 2.

Top features include :

  • Expressive Views including the new Razor View Engine
  • Powerful hooks with Dependency Injection and Global Action Filters
  • Rich JavaScript support with unobtrusive JavaScript, jQuery Validation, and JSON binding
  • Streamlined validation with improved Model validation

 

Happy new year.