Geeks With Blogs
My Blog

Update: I have updated the repository to include references to Fubu's Dlls instead of copying their code directly.

 

Here’s something fun that I’ve been playing with. A few weeks ago I saw Jeremy Miller’s post “Shrink your view’s with FubuMVC’s Html conventions” and knew I had to have this functionality in my Microsoft MVC applications. I pulled the source and started writing an adapter to make it work. I hope the FubuMVC guys don’t mind that I’ve borrowed some of their hard work.

What are Html Conventions?

Html conventions are essentially opinionated input builders. Instead of explicitly declaring what types of inputs and outputs will be rendered at design time, the designer instead simply states that there will be an input or output rendered (or possibly not rendered, actually). In code this might look like:

 
<% Html.LabelFor(x => x.Price) %>: <% Html.InputFor(x => x.Price) %>

The input builders available in MVCContrib and MVC 2 (as far as I know) are rendered using templates. You can define a String.aspx file that contains a text box or a Datetime.aspx that contains a date picker. What this means is that the output is relatively static unless you flood your template with conditional logic*. With FubuMVC’s input builders, you have the flexibility of building an HtmlTag tree at runtime. This allows you to define modifiers that alter the output, for example tagging an input with a “required” css class if the underlying view model property has a Required attribute or adding blank options to a drop down list.

*I think you can probably match Fubu’s functionality with MVCContrib, but at the cost of readability.

Why Use Html Conventions?

I use Html conventions in my applications because they provide a single point where I can immediately alter the appearance and functionality of the site. I don’t need to modify every view if I decide to swap out one style of date picker with another.

In my applications, I use the view model to define a contract between the view and the remainder of the application (controller + action filters) describing what data will be available. On the view model itself, I provide attribute annotations where necessary to provide the view with metadata about how to render certain controls. For instance I have BigTextAttribute which renders a text area; WithBlankOption which renders a blank drop down option; and AjaxOptions which renders an empty drop down that get’s populated by AJAX.

How They Work

In your IoC container you must register a class that provides the convention configuration. This class defines tag builders and modifiers that get executed based on whether they match the property/input being rendered. The render engine finds the first tag builder that matches the input and executes that. Then it finds all the modifiers which match the input and executes them on the tag. Lastly this tag (tree) is ToStringed and added to the page markup.

Sample Conventions

Here are the conventions that I am using in a production application right now.

 
public PMHtmlConventions()
{
	Editors.IfPropertyTypeIsEnum().BuildBy(EnumDropDownBuilder.Build);
	Editors.IfPropertyIs<bool>().BuildBy(req => new CheckboxTag(req.Value<bool>()));
	Editors.IfPropertyIs<DateTime?>().BuildBy(req => new CalendarTextBox(req.ElementId, req.Value<DateTime?>()));

	Editors.IfPropertyIs<DateTime>().BuildBy(req => new CalendarTextBox(req.ElementId, req.Value<DateTime>()));
	Editors.IfPropertyHasAttribute<AjaxOptionsAttribute>().BuildBy(SelectBuilder.AjaxBuild);
	Editors.IfPropertyHasAttribute<OptionsFromAttribute>().BuildBy(SelectBuilder.Build);
	Editors.IfPropertyHasAttribute<BigTextAttribute>().BuildBy(BigTextBuilder.Build);

	// Default text box
	Editors.Always.BuildBy(TagActionExpression.BuildTextbox);

	Editors.Always.Modify(AddElementName);

	Displays.Always.BuildBy(req => new HtmlTag("span").Text(req.StringValue()));
	Labels.Always.BuildBy(req => new HtmlTag("span").Text(LabelingConvention.GetLabelText(req.Accessor)).Id("label{0}".ToFormat(req.Accessor.Name)));
}

And an example of what a drop down builder looks like.

public class SelectBuilder
{
	public static HtmlTag Build(ElementRequest req)
	{
		return new SelectTag(BuildOptions(req)).Id(req.ElementId);
	}

	private static Action<SelectTag> BuildOptions(ElementRequest req)
	{
		var fromAttribType = typeof(OptionsFromAttribute);
		var fromAttrib = req.Accessor.GetAttribute<OptionsFromAttribute>();
		if (fromAttrib == null)
		{
			throw new Exception(string.Format("Expected property '{0}' to have decorator: {1}.", req.ElementId, fromAttribType.Name));
		}
		var fromProperty = req.Accessor.DeclaringType.GetProperties().FirstOrDefault(p => p.Name == fromAttrib.PropertyName);
		if (fromProperty == null)
			{
			throw new Exception(string.Format("Could not find options source property '{0}' on type '{1}'", fromAttrib.PropertyName, req.Accessor.DeclaringType.Name));
			}
		var optionPairs = fromProperty.GetGetMethod().Invoke(req.Model, null) as IDictionary;
		var hasBlankOption = req.Accessor.HasAttribute<WithBlankOption>();
		return tag =>
		   {
			   foreach (var key in optionPairs.Keys)
			   {
				   tag.Option(optionPairs[key].ToString(), key);
			   }
			   if (req.RawValue != null)
			   {
				   tag.SelectByValue(req.RawValue.ToString());
			   }
		   };
	}
}

The source

You can download the source from here. I included the assemblies necessary to get this working. The FubuMVC source is available here. There is a sample web application. Navigate to /Product/Index to see the very simple output.

Note that if you would like to use this with Spark you will need to define your own typed base view as described here.

 

I hope all of you enjoy working with conventions as much as I have! Thanks for the feedback.

Posted on Wednesday, February 24, 2010 11:26 PM | Back to top


Comments on this post: Using FubuMVC’s Html Conventions in Microsoft MVC

# re: Using FubuMVC’s Html Conventions in Microsoft MVC
Requesting Gravatar...
Can I reiterate that I think you'd be better off using the FubuMVC.UI dll as is and just putting the shim into another assembly? FubuMVC.UI is still evolving and you might be missing out on a lot of new things by just copying the code, plus you're own extensions go out into space.
Left by Jeremy D. Miller on Feb 25, 2010 7:09 AM

# re: Using FubuMVC’s Html Conventions in Microsoft MVC
Requesting Gravatar...
I guess I misunderstood you before, but that's a good point that FubuMVC.UI is still changing. This actually grew out of me learning how the UI assembly works, so I didn't expect it to be fancy. I'll make a note of this and post an update soon.
Left by Ryan Ohs on Feb 25, 2010 8:02 AM

Your comment:
 (will show your gravatar)


Copyright © Ryan Ohs | Powered by: GeeksWithBlogs.net