Friday, April 30, 2010

MVC extensions: Cascading dropdown control

In my previous post here, I explained how to implement cascading dropdown by adding additional html custom attributes and use it to populate the sibling dropdown with the help of jQuery. I also showed a sample code of new extension control “CascadeDropDown” that will help us to avoid the hassle of writing these attributes manually.   

Today I will continue with the CascadeDropDown control to implement the view data binding, this is important for populating the top level cascade dropdown from the view data in the same manner as the original MVC htmlhelper dropdown.

/// <summary>
/// Render cascading dropdown control 
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="name">Rendered html element name</param>
/// <param name="defaultText">The default initial text to display at top</param>
/// <param name="actionName">The name of the action used to fill the sibling dropdown</param>
/// <param name="controllerName">The action controller name of the action used to fill the sibling dropdown</param>
/// <param name="siblingControlId">The sibling dropdown Id to fill on selected item change</param>
/// <param name="siblingdefaultText">The sibling dropdown default text</param>
/// <param name="siblingdefaultValue">The sibling dropdown default value</param>
/// <returns>string</returns>
public static string CascadeDropdown(this HtmlHelper htmlHelper, string name, string defaultText, string actionName, string controllerName, string siblingControlId, string siblingdefaultText, string siblingdefaultValue)
{  
    var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
    string action = urlHelper.Action(actionName, controllerName, null);
    StringBuilder sb = new StringBuilder();
    TagBuilder inputBuilder = new TagBuilder("select");
    inputBuilder.GenerateId(name);
    inputBuilder.MergeAttribute("name", name);
    inputBuilder.MergeAttribute("dataurl", action);
    inputBuilder.MergeAttribute("targetcontrolid", siblingControlId);
    if (siblingdefaultText != null) inputBuilder.MergeAttribute("deftext", siblingdefaultText);
    if (siblingdefaultValue != null) inputBuilder.MergeAttribute("defvalue", siblingdefaultValue);
    if (htmlHelper.ViewData.ContainsKey(name))
    {
        sb.Append(inputBuilder.ToString(TagRenderMode.StartTag));
        
        SelectList data = (SelectList)htmlHelper.ViewData[name];
        if (defaultText != null) sb.AppendFormat("<option value=\"\">{0}</option>", defaultText);
        foreach (SelectListItem item in data)
        {
            if (item.Selected)
            {
                sb.AppendFormat("<option value=\"{0}\" selected>{1}</option>", item.Value, item.Text);
            }
            else
            {
                sb.AppendFormat("<option value=\"{0}\">{1}</option>", item.Value, item.Text);
            }
        }
        sb.Append(inputBuilder.ToString(TagRenderMode.EndTag));
    }
    else
    {
        inputBuilder.MergeAttribute("disabled", "disabled");
        sb.Append(inputBuilder.ToString(TagRenderMode.SelfClosing));
    }
    
    return sb.ToString();        
}

Now we need to create a select list and set it in the View Data [with the same name as the cascade dropdown].

ViewData["Countries"] = new SelectList(context.Country, "CountryId", "CountryName");
 

Note: MVC extension Html helpers must be implemented in a static class. 

No comments: