Project Description
The MVCViewComponents project provides a set of base classes that allow ASP.NET MVC developers to easily build powerful, advanced reusable HTML helpers in the form of fluent View Components that can be redistributed to other projects.

MVC ViewComponents: Power Up Your HTML Helpers!

If you have ever written web applications using ASP.NET WebForms and moved to ASP.NET MVC, you probably loved its clean seperation of concerns and all the patterns that come with it that help improve the quality of your code and application.
You probably also felt a bit uncertain about having to seemlingly lost the vast array of rich UI component libraries.
Along the way, moving through MVC 1 to MVC 3, writing views have become a greatly improved experience, thanks to the EditorTemplates / DisplayTemplates support, the Razor view engine and its ability to write templated HTML helpers, amongst other improvements.

However, one thing that I have always been missing is a core set of UI base components on which we can build more advanced / derived / composite UI components. The MVC Futures project contains an experimental API for controls, but that is still based on WebForms WebControls (although the authors try to hide that fact, and will without a doubt eventually replace the base classes with pure view control base classes).
But what I think we really want is to be able to easily write both simple as well as advanced HTML helpers that are flexible and leverage the view engine's ability to render the view model with the desired Partial View.
We don't want to write a static piece of HTML in C# code, only maybe just to provide a default basic HTML output (which might even be loaded from a precompiled, embedded razor view).
At the same time, the HTML helper should be able to support templates, in much the same way that is possible with WebForms CustomControls and if you have ever written a theme for Orchard CMS, you will be familiar with the concept of shapes (a shape is a viewmodel that can be "skinned" using templates / partial views).

As you are probably aware of, templated Razor delegates are currently possible: http://haacked.com/archive/2011/02/27/templated-razor-delegates.aspx.

So why would we need anything else?

If you have used HTML helpers, or even written a few yourself, you may have come to dislike having to pass all the possible options as arguments. For simple HTML helpers, this is no big deal.
But what if you wanted to write a reusable HTML helper, that support many options and settings, content templates for certain areas within your UI part, and also supports being skinned (rendered using different partial views)?
Passing everything in as method arguments in these cases is not going to be very pretty.

So what do I propose then? Write your HTML Helpers using a Fluent interface, of course!
And not just that: a component library allows for building a rich set of UI components that inherit UI functionality from base classes, thus reusing common properties of any UI widget.
And using a component based approach you could easily create composite UI components.

Fluent HTML Helpers

I can hear some of you screaming: We already have MvcContrib.FluentHtml! Check it out on: http://mvccontrib.codeplex.com/wikipage?title=FluentHtml&referringTitle=Documentation.
And you are right, of course. However, MvcViewComponents takes a bit of a different approach: MvcContrib's primary goal is to provide a set of ready to use components for MVC, while the primary goal of MvcViewComponents is to provide a set of base classes and interfaces that allow easy authoring of your own reusable, skinnable, UI widgets using a fluent syntax.
Also, the core API will never get into the way: the fluent wrappers are explicitly separate classes.

And of course if I did want to use MVCContrib.FluentHTML, for example to take advantage of the HTML helpers that make use of model state, I could easily encapsulate those helpers.
I could even create advanced composite HTML Helpers that combine existing ViewComponents, like I can with Composite Controls in WebForms!

ViewComponents: HTML Helpers on Steroids

HTML Helpers that support fluent interfaces, content templates, skinning (templating) and composition are more than just helpers: they are MVC View Components.
The term ViewComponents describes perfectly what we are building: reusable pieces of UI that are skinnable (using Partial Views) and redestributable. Just like the CustomControls of ASP.NET WebForms.

ViewComponents are inspired by Telerik's approach to UI Components for MVC (http://www.telerik.com/products/aspnet-mvc.aspx), combined with the powerful technique of Templated Razor Delegates.

In short, a ViewComponent is:
  • Reusable / redistributable;
  • Skinnable;
  • Easy to consume from Views (using fluent syntax) as well as easy to author (by using a simple set of base classes and interfaces);
  • Able to provide rich UI components;
  • Using a Component based model that will allow you to create shared functionality from which more specific components inherit from;
  • In the end nothing more than a ViewModel created and populated by its Fluent wrapper. Just the ViewModel is used within the skin / partial view.

Example: using a ViewComponent from within a view

In this example, we have written a ViewComponent called Menu. We can use it from within our views like this:

   @Html.MyWidgetScope().Menu( menu => menu
                                                     .Name ("Main Menu")
                                                     .AddItem ( item => item.Text ("Home").Action("Index", "Home")
                                                     .AddItem ( item => item.Text ("About Us").Action("AboutUs", "Home")
                                                     .AddItem ( item => item.Text ("Products").Action("List", "Product")
                                                                                 .AddItem( subItem => subItem.Text("Games", "Product")
                                                                                 .AddItem( subItem => subItem.Text("Apps", "Product")
                                                                                 .AddItem( subItem => subItem.Text("Videos", "Product")
                                                                                                                  .ContentTemplate(@<div>List of popular videos</div>)
       )


To create such a Menu ViewComponent, you would write four classes: the Menu and MenuItem classes themself, and their fluent wrappers called ComponentBuilders:

   public class Menu : UIComponent
   {
      public string Name { get; set;}
      public List<MenuItem> Items { get; private set; }

      public Menu()
      {
         Items = new List<MenuItem>();
      }
   }

   public class MenuItem : UIComponent
   {
      public string Text { get; set;}
      public string ActionName { get; set;}
      public string ControllerName { get; set;}
      public object RouteValues { get; set;}
      public string NavigateUrl { get; set;}
      public Func<MenuItem, HelperResult> ContentTemplate { get; set; }
      public List<MenuItem> Items { get; private set; }

      public MenuItem()
      {
         Items = new List<MenuItem>();
      }
   }

   /*************************************************************************************************
   * ComponentBuilders (providing a nice fluent syntax around the ViewComponents)
   **************************************************************************************************/
   public class MenuBuilder : UIComponentBuilder<Menu, MenuBuilder>
   {
      public MenuBuilder Name(string value)
      {
          return SetProperty( component => component.Name = value); // Set the Name property of the Menu component and return the builder in one line!
      }
      
      public MenuBuilder AddItem(Action<MenuItemBuilder> itemBuilder)
      {
          return AddItem( component => component.Items, CreateAndExecuteBuilder(itemBuilder).Component); // Create, setup and add a new MenuItem, then return the MenuBuilder in just one line!
      }
   }

   public class MenuItemBuilder : UIComponentBuilder<MenuItem, MenuItemBuilder>
   {
      public MenuItemBuilder Text(string value)
      {
          return SetProperty( component => component.Text = value);
      }

      public MenuItemBuilder ActionName(string value)
      {
          return SetProperty( component => component.ActionName = value);
      }

      public MenuItemBuilder ControllerName(string value)
      {
          return SetProperty( component => component.ControllerName = value);
      }

      public MenuItemBuilder RouteValues(object value)
      {
          return SetProperty( component => component.RouteValues = value);
      }

      public MenuItemBuilder NavigateUrl(string value)
      {
          return SetProperty( component => component.NavigateUrl = value);
      }
      
      public MenuBuilder AddItem(Action<MenuItemBuilder> itemBuilder)
      {
          return AddItem( component => component.Items, CreateAndExecuteBuilder(itemBuilder).Component);
      }
   }


To support a nice fluent syntax it is required to write a ComponentBuilder, but as you see it is just a simple wrapper around your actual ViewComponent.
The UIComponent class is a simple abstract class that implement the IUIComponent interface:

   public interface IUIComponent
    {
        string ViewName { get; set; }
        IHtmlString ToString(HtmlHelper html);
    }

    public abstract class UIComponent : IUIComponent
    {
        public virtual string ViewName { get; set; }
        
        public virtual IHtmlString ToString(HtmlHelper html)
        {
            var viewName     = GetViewName();
            var result       = ViewEngines.Engines.FindPartialView(new ControllerContext(html.ViewContext.RequestContext, html.ViewContext.Controller), viewName);
            var sb           = new StringBuilder();

            using(var writer = new HtmlTextWriter(new IndentedTextWriter(new StringWriter(sb))))
            {
                html.ViewContext.ViewData.Model = this;

                if (result.View != null)
                {
                    result.View.Render(html.ViewContext, writer);
                }
                else
                {
                    RenderDefaultView(writer);
                }
            }

            return new HtmlString(sb.ToString());
        }

        private string GetViewName()
        {
            return !string.IsNullOrWhiteSpace(ViewName) ? ViewName : GetType().Name;
        }

        protected void RenderDefaultView(HtmlTextWriter writer)
        {
            var tag = new TagBuilder("div");
            writer.WriteLine(tag.ToString());
        }
    }


What the UIComponent class does is provide the functionality to render itself, by default looking for a PartialView that has the same name as the typename of your ViewComponent (which can of course be overridden or specified by the caller).
Please note that I will soon seperate this functionality into a dedicated renderer class, to adhere to the Single Responsibility Principle. That will make the UIComponent class even simpler.
Note also that I have provided a basic means of allowing the component author to provide a default HTML string in case no Partial View is found in the MVC application.
One thing I'm investigating is to be able to use precompiled Razor views within your component library that will be shipped as a default template.

To attach your ViewComponent to the Html property within your own scope, you need to write at least one static class that provides the extensions:

   public static class HtmlHelperHooks
   {
      public static UIComponentFactory MyUIScope(this HtmlHelper html)
      {
         return new UIComponentFactory(html);
      }

      public static MenuBuilder Menu(this UIComponentFactory factory, Action<MenuBuilder> menu)
      {
         return factory.CreateAndExecuteBuilder(menu);
      }
   }


As you can see, you attach your ViewComponents to the UIComponentFactory class as extension methods instead of directly to the HtmlHelper.
This enables you to create one or more "scopes" for your UI components, which helps prevent cluttering the Html object extension methods.

To use the component from within a view, it goes something like this:

@Html.MyUIScope().Menu( menu => menu.Name("Main Menu").AddItem( item => item.Text("Item 1")).AddItem( item => item.Text("Item 2") 


Obviously, you could enhance the fluent syntax a bit by mixing in some simple method overloads that accept commonly used properties:

   public class MenuItemBuilder : UIComponentBuilder<MenuItem, MenuItemBuilder>
   {
      ....
      public MenuItemBuilder AddItem(string text, string actionName, string controllerName = null, object routeValues = null, Action<MenuItemBuilder> itemSetup)
      {
          var itemBuilder = CreateAndExecuteBuilder(itemSetup).Text(text).ActionName(actionName).ControllerName(controllerName).RouteValues(routeValues);
          return AddItem( component => component.Items, itemBuilder.Component);
      }

   }


Okay, so you have written a nice set of ViewComponents, but where is the HTML?
That should simply go into partial views, which take the ViewComponent, e.g. Menu and MenuItem, as the Model type.
Using the powerful Razor syntax you will be able to easily componentize your HTML partial views.

Try it out and let me know what you think!
Currently I only have source for this core library, but I will be sure to create some sample implementations of widgets such as buttons, menus, progressbar, etc.
Perhaps that's even gonna be a project on its own: a vast library of commonly used UI components. MvcComponentizing jQuery UI might be intersting as well, although Telerik has already done that.

Note: this project has a dependency on the FluentComponents library: fluentcomponents.codeplex.com

What do you say?

Last edited Aug 30, 2011 at 11:55 PM by sfmskywalker, version 19