Tuesday, February 28, 2012

HTML5 Cheat Sheet–Tags & Event Handler Attributes

HTML5 Cheat Sheet – Tags , Event Handler Attributes – made easy by InMotion Hosting.
Tags -
HTML5 Cheat Sheet - Tags

Event Handlers:
HTML5 Cheat Sheet - Event Handler Attributes

The original post from InMotion hosting also includes a sheet for browser support but it seems to be outdated.
To find out how your browser is doing and how do new and old, desktop and mobile  browser support HTML5, please visit – HTML5Test.com

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.