65.9K
CodeProject is changing. Read more.
Home

Custom HTML Helper with Page Mode Rendering

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1 vote)

Feb 3, 2019

CPOL

1 min read

viewsIcon

10556

Create custom HTML helper to manage Page Mode like View (Readonly) and Edit/Add (Editable)

Introduction

How do we make an entire Web Page editable or readonly based on certain conditions without using a bunch of If else conditions? While searching for an answer to this question, I came across lots of ways, i.e., as follows:

  1. We can create two different views and can call based on the required condition:
    @if (readonly)
    {
         @Html.DisplayForView()
    }
    else
    {
         @Html.EditorForView()
    }

    In this, we will need to create two different partial views, which seems to be code redundancy.

  2. The second approach I found is to set the If else condition in the @Html.Control:
    @Html.EditorFor(x => x.Name,IsReadonly ? (object) new 
                { htmlAttributes = new { @readonly = "readonly"} }
                        : new { htmlAttributes = new { @readonly = ""} })
    
    In this case, if my form has multiple EditorFor, then it becomes a tedious job to do the conditioning and also, your view becomes clumsy and heavy.

So just to manage everything in one place, I created a Custom HTML Helper and set the readonly attribute based on the Page Mode which I managed to pass using ViewBag. Following is the code for the same.

Index.cshtml

@{
    Layout = null;
}
@using CustomHTMLHelperPageMode.Helper
@model CustomHTMLHelperPageMode.Models.User

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

    <link rel="stylesheet" 
     href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">

    <!-- jQuery library -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

    <!-- Latest compiled JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</head>
<body>
    <div>
        @using (Html.BeginForm())
        {
            <div class="container">
                <div class="form-group">
                    @Html.LabelFor(model => model.Name)
                    @Html.PageModeTextBoxFor(model => model.Name, 
                          new { htmlAttributes = new { @class = "form-control" } })
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Phone)
                    @Html.PageModeTextBoxFor(model => model.Phone, 
                          new { htmlAttributes = new { @class = "form-control" } })
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Email)
                    @Html.PageModeTextBoxFor(model => model.Email, 
                          new { htmlAttributes = new { @class = "form-control" } })
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Address)
                    @Html.PageModeTextBoxFor(model => model.Address, 
                          new { htmlAttributes = new { @class = "form-control" } })
                </div>

                @if (ViewBag.PageMode != "View")
                {
                    <input type="submit" value="Create" class="btn btn-default" />
                }

                </div>
                }
            </div>
</body>
</html>

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace CustomHTMLHelperPageMode.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult ViewIndex()
        {
            ViewBag.PageMode = "View";
            return View("Index");
        }
    }
}

PageModeTextBox.cs (Helper)

using System;
using System.Collections.Generic;
using System.Web.Mvc.Html;

namespace CustomHTMLHelperPageMode.Helper
{
    public static class PageModeTextBox
    {
        public static System.Web.Mvc.MvcHtmlString PageModeTextBoxFor<TModel, TValue>
       (this System.Web.Mvc.HtmlHelper<TModel> html,
       System.Linq.Expressions.Expression<Func<TModel, TValue>> expression,
       object htmlAttributes = null, bool readOnly = false)
        {

            System.Web.Mvc.ModelMetadata oModelMetadata =
                System.Web.Mvc.ModelMetadata.FromLambdaExpression(expression, html.ViewData);

            Dictionary<string, object> dynamicAttribute = new Dictionary<string, object>();

            foreach(var prop in htmlAttributes.GetType().GetProperties())
            {
                if(prop.Name== "htmlAttributes")
                {
                    var propValue = prop.GetValue(htmlAttributes, null);
                    foreach (var innerProp in propValue.GetType().GetProperties())
                    {
                        dynamicAttribute.Add(innerProp.Name, innerProp.GetValue(propValue, null));
                    }
                }
                else
                {
                    dynamicAttribute.Add(prop.Name, prop.GetValue(prop));
                }
            }            

            if (html.ViewBag.PageMode == "View")
            {
                if (dynamicAttribute.ContainsKey("readonly") == false)
                {
                    dynamicAttribute.Add("readonly", "read-only");
                }
            }     

            return (html.TextBoxFor(expression, dynamicAttribute));
        }
    }
}

User.cs (Model)

using System.ComponentModel;

namespace CustomHTMLHelperPageMode.Models
{
    public class User
    {
        [DisplayName("Name")]
        public string Name { get; set; }
        [DisplayName("Contact No")]
        public string Phone { get; set; }
        [DisplayName("Email")]
        public string Email { get; set; }
        [DisplayName("Address")]
        public string Address { get; set; }
    }
}

Edit Mode

View Mode

The sample code is available on GitHub.

If any of the readers have an improved and better workflow for the above requirement, I would like to add the same to my knowledge.

CODE IT !!!