ASP.NET MVC UI Components (Continued)

In my last post, two important issues are raised

  1. The justification of having server side components for jQuery UI.
  2. The style of syntax.

The intension of my last post was to get the feedback of the type of syntax the ASP.NET MVC developer prefers, so I did not mention anything on the server side side integration, this might be the reason why few people were unable to find the benefits of this server side support. In this post, I will try to show few simple examples of the server side integration, lets say that you are creating a Task submit form, you can use the Slider as completed percent field instead of regular input field, like the following:

View:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Task>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Slider Value Form Submit Example
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% using(Html.BeginForm()){%>
        <fieldset>
            <legend>Submit Task</legend>
            <span style="color:Blue;font-weight:bold">
                <%= ViewData.Get<string>("successMessage") %>
            </span>
            <%= Html.ValidationSummary("Please correct the following errors:") %>
            <div>
                <label for="name">Name:</label>
            </div>
            <div style="margin-top:5px">
                <%= Html.TextBox("task.Name", null, new { style = "width:200px" })%>
            </div>
            <div style="margin-top:5px">
                <%= Html.ValidationMessage("task.Name")%>
            </div>
            <div style="margin-top:10px">
                Completed: <span id="completedPercent" style="color:Black"></span>%
            </div>
            <div style="margin-top:5px;width:200px">
                <% Html.jQuery().Slider()
                                .Name("task.Completed")
                                .UpdateElements("#completedPercent")
                                .Render(); %>
            </div>
            <div style="margin-top:5px">
                <%= Html.ValidationMessage("task.Completed")%>
            </div>
            <div style="margin-top:10px">
                <input type="submit" value="Submit"/>
            </div>
        </fieldset>
    <% }%>
</asp:Content>

Controller:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult FormSubmitWithValue()
{
    return View(new Task());
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult FormSubmitWithValue([Bind]Task task)
{
    if (string.IsNullOrEmpty(task.Name))
    {
        ModelState.AddModelError("task.Name", "Name cannot be blank.");
    }

    if (task.Completed <= 0)
    {
        ModelState.AddModelError("task.Completed", "Invalid task complete percent.");
    }

    if (ModelState.IsValid)
    {
        //Save here;
        ViewData.Set("successMessage", "Task saved successfully.");
    }

    return View(task);
}

[Live Version]

Check that (line 26- 29 in the above View) you are using the same kind of naming as you do in strongly typed view for regular input fields, you can use the slider for non-strongly typed view too.

In case of Slider is ranged, we can use the following:

View:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Slider Values Form Submit Example
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% using(Html.BeginForm()){%>
        <fieldset>
            <legend>Submit Department</legend>
            <span style="color:Blue;font-weight:bold">
                <%= ViewData.Get<string>("successMessage") %>
            </span>
            <%= Html.ValidationSummary("Please correct the following errors:") %>
            <div>
                <label for="name">Name:</label>
            </div>
            <div style="margin-top:5px">
                <%= Html.TextBox("name", null, new { style = "width:200px" })%>
            </div>
            <div style="margin-top:5px">
                <%= Html.ValidationMessage("name") %>
            </div>
            <div style="margin-top:10px">
                Salary: $<span id="rangeFrom" style="color:Black"></span> - $<span id="rangeTo" style="color:Black"></span>
            </div>
            <div style="margin-top:5px;width:600px">
                <% Html.jQuery().Slider()
                                .Name("salary")
                                .Range(jQuerySliderRange.True)
                                .Values(1000, 2500)  //Initial value
                                .UpdateElements("#rangeFrom", "#rangeTo")
                                .Minimum(1000)
                                .Maximum(10000)
                                .Steps(500)
                                .Render(); %>
            </div>
            <div style="margin-top:5px">
                <%= Html.ValidationMessage("salary")%>
            </div>
            <div style="margin-top:10px">
                <input type="submit" value="Submit"/>
            </div>
        </fieldset>
    <% }%>
</asp:Content>

Controller:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult FormSubmitWithValues()
{
    return View();
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult FormSubmitWithValues(string name, IList<int> salary)
{
    if (string.IsNullOrEmpty(name))
    {
        ModelState.AddModelError("name", "Name cannot be blank.");
    }

    if (salary.Count != 2)
    {
        ModelState.AddModelError("salary", "Invalid salary range.");
    }

    if (salary.Count == 2)
    {
        if (salary[0] <= 1000)
        {
            ModelState.AddModelError("salary", "Salary minimum range should be greater than 1000.");
        }

        if (salary[1] <= 2500)
        {
            ModelState.AddModelError("salary", "Salary maximum range should be greater than 2500.");
        }
    }

    if (ModelState.IsValid)
    {
        //Save here;
        ViewData.Set("successMessage", "Department saved successfully.");
    }

    return View();
}

[Live Version]

Since the slider is a range slider, it will have an array of values, that is why the controllers accepts IList<int> for the slider. This will also work for strongly typed view.

You can also use the ProgressBar to auto retrieve the value directly from ViewData, for example,

View:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    ProgressBar Auto Retrieve Value Example
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% Html.jQuery().ProgressBar()
                    .Name("myProgressBar")
                    .Render(); %>
</asp:Content>

Controller:

public ActionResult AutoRetrieve()
{
    ViewData["myProgressBar"] = 40;

    return View();
}

[Live Version]

As mentioned in the past that the goal of this component is to add some RAD support for the ASP.NET MVC Developers. For example, in the original jQuery UI both Slider and ProgressBar does not have any built-in support to show the value(s) in an associated html element(s), but it does have this support to attach any html elements to show the numeric value(s), check the UpdateElements method in the above (works on jQuery Selector), behind the scene it generates the necessary javascript codes to hook the events and update the elements, the same is true for the above slider form submit examples, it generates the required hidden input(s) to support form submit. It will not discourage you from writing javascript codes, instead it automates some common repetitive tasks. The next thing of my previous blog post was the benefits of server side generating the necessary html and javascript codes even when there is no integration of server side like the above slider or progressbar, to demonstrate it, I will first create a dynamic jQuery UI tab with raw html and javascripts, lets assume the tab items are disabled and selected based upon some condition.

Raw Html and JavaScript:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IList<DynamicTabContent>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Tab Dynamic Item Example
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% var disabledIndexes = new List<string>(); %>
    <% var selectedIndex = 0; %>
    <div id="tabs">
        <ul>
            <% for (var i = 0; i < Model.Count; i++) {%>
                <li><a href="#section-<%= i %>"><%= Html.Encode(Model[i].Header)%></a></li>
            <% }%>
        </ul>
        <% for (var i = 0; i < Model.Count; i++) {%>
            <% var dynamicContent = Model[i]; %>
            <div id="section-<%= i %>">
                <p><%= Html.Encode(dynamicContent.Content) %></p>
            </div>
            <% if (dynamicContent.IsDisabled)
               {
                   disabledIndexes.Add(i.ToString());
               }
               //The last selected will win
               if (dynamicContent.IsSelected && (i > selectedIndex))
               {
                   selectedIndex = i;
               }
            %>
        <% }%>
    </div>
    <script type="text/javascript">
        $(document).ready(function(){
            $('#tabs').tabs({
                                selected : <%= selectedIndex %>,
                                disabled : [<%= string.Join(", ", disabledIndexes.ToArray()) %>]
                            });
        });
    </script>
</asp:Content>

In the above, we are first iterating the Model to generate the tab headers, next we are again iterating it to generate the content panes and this time we are also populating the disabled index list and selecting the selected index. At last, we are dumping the selected index and converting the disabled indexes to a comma delimited string to create the tab in javascript.

Now lets see how this can simplify the above situation, we can do the exact same thing with the following codes.

Simple Fluent:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IList<DynamicTabContent>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Tab Dynamic Item Example
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <% Html.jQuery().Tab().Name("tabs")
                          .Items(item =>
                                         {
                                             for(var i = 0; i < Model.Count; i++)
                                             {
                                                 var dynamicContent = Model[i];

                                                 item.Create()
                                                     .HeaderText(dynamicContent.Header)
                                                     .Content(() =>
                                                                      {%>
                                                                        <p><%= Html.Encode(dynamicContent.Content) %></p>
                                                                      <%}
                                                             )
                                                     .Selected(dynamicContent.IsSelected)
                                                     .Disabled(dynamicContent.IsDisabled);
                                             }
                                         }
                                )
                          .Render(); %>
</asp:Content>

[Live Version]

As you can see it is much more simpler and concise and with the above, we do not have to iterate the model again and again, no need to maintain the selected index and disabled index list, the component takes these responsibility and generates the Tab. I hope this clarifies the issue.

The second issue is the style of syntax, first, let me post the result of my previous Poll (total 48 votes):

  • Regular Methods (9 votes, 19%)
  • Simple Fluent (14 votes, 29%)
  • Progressive Fluent(25 votes, 52%)

Though the progressive has more votes, but I think once you are familiar with the syntax, the verboseness of the progressive fluent will start to irritate you and this is the reason why I have changed it to simple fluent from progressive fluent. You can download the latest version from the bottom of the post. Another interesting syntax proposed my Chad Myer. The difference between mine with this is that it first creates the component, but instead of returning the component it provides an Action to configure the component. Let us do a side by side comparison of this syntax with the simple fluent, here we will see the basic tab:

Factory + Action Syntax:

<% Html.jQuery<jQueryTab>(tab =>
                                       {
                                           tab.Name = "myTab";
                                           tab.Items(items =>
                                                             {
                                                                 items.Create(item =>
                                                                                        {
                                                                                            item.HeaderText = "Nunc tincidunt";
                                                                                            item.Content = () =>
                                                                                                                {%>
                                                                                                                     <p>
                                                                                                                         Proin elit arcu, rutrum commodo, vehicula tempus,
                                                                                                                         commodo a, risus. Curabitur nec arcu. Donec
                                                                                                                         sollicitudin mi sit amet mauris. Nam elementum
                                                                                                                         quam ullamcorper ante. Etiam aliquet massa et
                                                                                                                         lorem. Mauris dapibus lacus auctor risus. Aenean
                                                                                                                         tempor ullamcorper leo. Vivamus sed magna quis
                                                                                                                         ligula eleifend adipiscing. Duis orci. Aliquam
                                                                                                                         sodales tortor vitae ipsum. Aliquam nulla. Duis
                                                                                                                         aliquam molestie erat. Ut et mauris vel pede
                                                                                                                         varius sollicitudin. Sed ut dolor nec orci
                                                                                                                         tincidunt interdum. Phasellus ipsum. Nunc
                                                                                                                         tristique tempus lectus.
                                                                                                                    </p>
                                                                                                                 <%};
                                                                                        }
                                                                               );

                                                                 items.Create(item =>
                                                                                        {
                                                                                            item.HeaderText = "Proin dolor";
                                                                                            item.Content = () =>
                                                                                                                {%>
                                                                                                                     <p>
                                                                                                                        Morbi tincidunt, dui sit amet facilisis feugiat,
                                                                                                                        odio metus gravida ante, ut pharetra massa metus
                                                                                                                        id nunc. Duis scelerisque molestie turpis. Sed
                                                                                                                        fringilla, massa eget luctus malesuada, metus eros
                                                                                                                        molestie lectus, ut tempus eros massa ut dolor.
                                                                                                                        Aenean aliquet fringilla sem. Suspendisse sed
                                                                                                                        ligula in ligula suscipit aliquam. Praesent in
                                                                                                                        eros vestibulum mi adipiscing adipiscing. Morbi
                                                                                                                        facilisis. Curabitur ornare consequat nunc. Aenean
                                                                                                                        vel metus. Ut posuere viverra nulla. Aliquam erat
                                                                                                                        volutpat. Pellentesque convallis. Maecenas feugiat,
                                                                                                                        tellus pellentesque pretium posuere, felis lorem
                                                                                                                        euismod felis, eu ornare leo nisi vel felis.
                                                                                                                        Mauris consectetur tortor et purus.
                                                                                                                    </p>
                                                                                                                 <%};
                                                                                        }
                                                                               );

                                                                 items.Create(item =>
                                                                                        {
                                                                                            item.HeaderText = "Aenean lacinia";
                                                                                            item.Content = () =>
                                                                                                                {%>
                                                                                                                    <p>
                                                                                                                        Mauris eleifend est et turpis. Duis id erat.
                                                                                                                        Suspendisse potenti. Aliquam vulputate, pede vel
                                                                                                                        vehicula accumsan, mi neque rutrum erat, eu congue
                                                                                                                        orci lorem eget lorem. Vestibulum non ante. Class
                                                                                                                        aptent taciti sociosqu ad litora torquent per
                                                                                                                        conubia nostra, per inceptos himenaeos. Fusce
                                                                                                                        sodales. Quisque eu urna vel enim commodo
                                                                                                                        pellentesque. Praesent eu risus hendrerit ligula
                                                                                                                        tempus pretium. Curabitur lorem enim, pretium nec,
                                                                                                                        feugiat nec, luctus a, lacus.
                                                                                                                    </p>
                                                                                                                    <p>
                                                                                                                        Duis cursus. Maecenas ligula eros, blandit nec,
                                                                                                                        pharetra at, semper at, magna. Nullam ac lacus.
                                                                                                                        Nulla facilisi. Praesent viverra justo vitae neque.
                                                                                                                        Praesent blandit adipiscing velit. Suspendisse
                                                                                                                        potenti. Donec mattis, pede vel pharetra blandit,
                                                                                                                        magna ligula faucibus eros, id euismod lacus dolor
                                                                                                                        eget odio. Nam scelerisque. Donec non libero sed
                                                                                                                        nulla mattis commodo. Ut sagittis. Donec nisi
                                                                                                                        lectus, feugiat porttitor, tempor ac, tempor vitae,
                                                                                                                        pede. Aenean vehicula velit eu tellus interdum
                                                                                                                        rutrum. Maecenas commodo. Pellentesque nec elit.
                                                                                                                        Fusce in lacus. Vivamus a libero vitae lectus
                                                                                                                        hendrerit hendrerit.
                                                                                                                    </p>
                                                                                                                 <%};
                                                                                        }
                                                                               );
                                                             }
                                                    );
                                       }
                            ); %>

Simple Fluent:

<% Html.jQuery().Tab()
                .Name("myTab")
                .Items(items =>
                              {
                                 items.Create()
                                      .HeaderText("Nunc tincidunt")
                                      .Content(() =>
                                                     {%>
                                                         <p>
                                                             Proin elit arcu, rutrum commodo, vehicula tempus,
                                                             commodo a, risus. Curabitur nec arcu. Donec
                                                             sollicitudin mi sit amet mauris. Nam elementum
                                                             quam ullamcorper ante. Etiam aliquet massa et
                                                             lorem. Mauris dapibus lacus auctor risus. Aenean
                                                             tempor ullamcorper leo. Vivamus sed magna quis
                                                             ligula eleifend adipiscing. Duis orci. Aliquam
                                                             sodales tortor vitae ipsum. Aliquam nulla. Duis
                                                             aliquam molestie erat. Ut et mauris vel pede
                                                             varius sollicitudin. Sed ut dolor nec orci
                                                             tincidunt interdum. Phasellus ipsum. Nunc
                                                             tristique tempus lectus.
                                                        </p>
                                                     <%}
                                                );

                                  items.Create()
                                       .HeaderText("Proin dolor")
                                       .Content(() =>
                                                     {%>
                                                         <p>
                                                            Morbi tincidunt, dui sit amet facilisis feugiat,
                                                            odio metus gravida ante, ut pharetra massa metus
                                                            id nunc. Duis scelerisque molestie turpis. Sed
                                                            fringilla, massa eget luctus malesuada, metus eros
                                                            molestie lectus, ut tempus eros massa ut dolor.
                                                            Aenean aliquet fringilla sem. Suspendisse sed
                                                            ligula in ligula suscipit aliquam. Praesent in
                                                            eros vestibulum mi adipiscing adipiscing. Morbi
                                                            facilisis. Curabitur ornare consequat nunc. Aenean
                                                            vel metus. Ut posuere viverra nulla. Aliquam erat
                                                            volutpat. Pellentesque convallis. Maecenas feugiat,
                                                            tellus pellentesque pretium posuere, felis lorem
                                                            euismod felis, eu ornare leo nisi vel felis.
                                                            Mauris consectetur tortor et purus.
                                                        </p>
                                                     <%}
                                                );

                                  items.Create()
                                       .HeaderText("Aenean lacinia")
                                       .Content(() =>
                                                     {%>
                                                        <p>
                                                            Mauris eleifend est et turpis. Duis id erat.
                                                            Suspendisse potenti. Aliquam vulputate, pede vel
                                                            vehicula accumsan, mi neque rutrum erat, eu congue
                                                            orci lorem eget lorem. Vestibulum non ante. Class
                                                            aptent taciti sociosqu ad litora torquent per
                                                            conubia nostra, per inceptos himenaeos. Fusce
                                                            sodales. Quisque eu urna vel enim commodo
                                                            pellentesque. Praesent eu risus hendrerit ligula
                                                            tempus pretium. Curabitur lorem enim, pretium nec,
                                                            feugiat nec, luctus a, lacus.
                                                        </p>
                                                        <p>
                                                            Duis cursus. Maecenas ligula eros, blandit nec,
                                                            pharetra at, semper at, magna. Nullam ac lacus.
                                                            Nulla facilisi. Praesent viverra justo vitae neque.
                                                            Praesent blandit adipiscing velit. Suspendisse
                                                            potenti. Donec mattis, pede vel pharetra blandit,
                                                            magna ligula faucibus eros, id euismod lacus dolor
                                                            eget odio. Nam scelerisque. Donec non libero sed
                                                            nulla mattis commodo. Ut sagittis. Donec nisi
                                                            lectus, feugiat porttitor, tempor ac, tempor vitae,
                                                            pede. Aenean vehicula velit eu tellus interdum
                                                            rutrum. Maecenas commodo. Pellentesque nec elit.
                                                            Fusce in lacus. Vivamus a libero vitae lectus
                                                            hendrerit hendrerit.
                                                        </p>
                                                     <%}
                                                );
                              }
                       )
                .Render(); %>

To me the simple fluent is much simpler than Chad’s Factory + Action. But I think, it completely depends upon the personal preference. If you think, Factory + Action is much preferable than the simple fluent or even you want to revert back to the initial progressive fluent, do let me know I will change it  based upon your feedback.

[Live Version]

[Download Sample]

Shout it

7 Comments

  • Will there be source code available for these controls? Maybe a codeplex project? I would love to create support for other jquery plugins. Ex. Calendar, treeview, etc

  • In this case I don't really see what the other method buys you over the simple fluent interface. I think that unless I can see a situation where the newly proposed syntax makes things more simple, I would lean toward the fluent interface syntax.

  • "why few people were unable to find the benefits of this server side support"

    I think these people do not want server side support.

    They like having the Jquery/javascript build upon the html, they like the site to still run with javascript disabled.

    In this example, the Model and controller, will know nothing of the slider. The controller and model don't know how the user entered the value, it just gets the value, validates and saves .....

  • Mark this post does not has anything do with whether javascript is enabled/disabled.

  • Hi,

    @kazimanzurrashid , yeah I realise that. I was just trying to explain why people are "unable to see the benefits or server side support".
    Obviously I didn't do a very good job.
    How about: Having server side support for something that is only used for the UI is cross cutting concerns..


    On another note..
    I am actually more interested in how you will manage all the JavaScript includes, so if I use multiple components how will it ensure that, just the 1 javascript ref is included. I think the management of javascript includes, compression, combining, (management) etc is something missing from the framework.

  • @Mark: I have blogged about the Asset Management in ASP.NET MVC over http://weblogs.asp.net/rashid/archive/2009/04/28/script-and-css-management-in-asp-net-mvc.aspx and http://weblogs.asp.net/rashid/archive/2009/05/02/script-and-css-management-in-asp-net-mvc-part-2.aspx and it is fully backed into this component. On your previous comment, it is also possible to generate regular inputs from the server side instead of Slider when JS is turned off.

  • Hi Kazi, wow you have been busy :) Is the Griffin source code hosted somewhere public are you going to release this as open source? At the moment I am viewing the source in Reflector :) I have a project where I have to render different types of survey questions and it would be cool to learn from this as I have to use the web forms view engine..

Comments have been disabled for this content.