Since giving my talk at mvcconf, I’ve received numerous requests to talk a little more about the mysterious Bindings feature in Spark. To be clear, it’s only mysterious because it’s the newest feature in the framework and because of lack of documentation – that is until now. Louis Dejardin has outdone himself once again and put a very thorough piece of documentation together for this. I’m not going to rehash the official docs here, but instead suggest
where the practical application of this feature can lead to some very clean looking HTML indeed.
Where we came from
Ever since moving to writing code in Active Server Pages Classic(TM) coming from plain old HTML, I’ve always let out a small grimace every time I’ve been forced to write code in my views. At first it seemed like an awesome idea, and it was, but if you were working in ASP Classic way back when, then you’ll remember how easy it was to tie yourself in knots and burn gaping holes in any deadlines you may once have held dear. And you definitely didn’t want to be the unlucky sod dumped in the support team after delivery. Been there, done that - It hurt…a lot…
Moving to ASP.NET we were shepherded into an event driven “code behind” desktop-programming-esque way of building web pages, but you could still put some pretty compelling spaghetti code in the views that could thwart even the most nimble of code jockeys. Nowadays, we’ve become pretty numb to it, but if you dig deep enough, I’m sure you’ll still feel that pang of regret every time you serve up a fresh bowl of Tag Soup.
And so, we’ve been on this search… nay… pilgrimage to find the ideal mix of beauty, simplicity and power for creating the web user interface effectively without bastardising the mark-up readability, and at the same time, not sacrificing the hooks into the back-end server processes that web pages tend to rely on so heavily these days.
Without further ado, I present thee with Spark Bindings, the latest ingredient in the Spark View Engine picnic basket that will help make your views beautiful once again…
Show and tell
The best way to grok Bindings is to think of it as an IoC Container for your HTML - or a Mapper if you’re not into IoC, and if you’re not into IoC, then shame on you ;). For those with an IoC mindset, you’ll be no stranger to something like this in your Registry:
For<IFooInterface>().Use<FooConcreteType>();
Or for those who think in terms of mapping
Mapper.CreateMap<ModelType, ViewModelType>();
Basically you can see where I’m going here and in the simplest sense, Spark Bindings is a substitution engine where in can stipulate that Spark should comb your HTML and perform the substitutions that you specify. For example, you can stipulate that everywhere Spark sees something like:
<TextBox For=”UserName” />
it should actually use the following HTML Helper behind the scenes:
Html.TextBoxFor(x => x.@For)
where “@For” is the parameter with value “UserName” you specified in your HTML as an attribute. NEAT! Isn’t it?
You see – this gives you the power to create or use existing HTML helpers of all descriptions in your assemblies that are normally invoked in a single line of code, and instead of polluting your HTML views with code bits and pieces, you can get clean looking HTML like this:
<Form class="form-default">
<ValidationSummary Message="Login was unsuccessful.
Please correct the errors and try again." ExcludePropertyErrors="true" />
<div class="editor-label">
<Label For="UserName" />
</div>
<div class="editor-field">
<TextBox For="UserName" /><ValidationMessage For="UserName"/>
</div>
<div class="editor-label">
<Label For="Password" />
</div>
<div class="editor-field">
<Password For="Password" /><ValidationMessage For="Password" />
</div>
<div class="editor-label">
<CheckBox For="RememberMe" />
<Label For="RememberMe" />
</div>
<input type="submit" value="Log On" />
</Form>
Let’s quickly go through the interesting bits of the example above. The first thing you’ll notice is that it’s very difficult to spot the code – the abstractions (i.e. Bindings) are doing a very good job of blending in with the HTML, you know, exactly the way HTML was always meant to be! ;)
Secondly, I’m sure you noticed that you probably haven’t ever seen an HTML <Form> Tag without and action attribute or JavaScript hook of sorts right? And you definitely know that there’s no such thing as a <ValidationSummary> Tag in the HTML spec right? That’s right, you can both overload existing HTML Tags (with extra attributes or provide conventions for existing attributes) and invent new Tags of your choosing and bind those to the code you’d like to substitute in their place.
Any parameter that you may have for your HTML helpers can be fed through attributes and child nodes. You can keep it as simple as you like, or turn it up to eleven and really unleash the power under the hood here. The point remaining – it will STILL look and feel like HTML, instead of code. I won’t be able to go into all the powerful things you’re able to do in one post, but I hope I can give you enough of a taster that you’ll want to try it out for yourself.
Power of Abstraction
From the above code, you see that there could be absolutely any line of code hiding behind that <TextBox For=”UserName” /> binding. You don’t have to use an ASP.NET MVC helper, you could hook up the FubuMVC HtmlTags implementation instead if that’s what you prefer. Do you see that by binding these, you have just been provided with a way to write your views once, and replace the code bits behind the bindings with an entirely new library of product helpers simply by modifying the Bindings.xml – telerik anyone?
Power to Invent new HTML Tags
Don’t think of it as being limited to making HTML Forms prettier. You can also see from the code above that you’re able to create other HTML Tags you’ve always wanted! If you want a <FizzBuzz> Tag, there’s nothing stopping you. What’s more is that by putting that Tag abstraction in, this give you the freedom to replace your FizzBuzz Helper with your new and and improved FizzierBuzzing Helper at a moments notice throughout your views just by modifying the Binding in the xml. But that’s just the start. This thing has oodles of untapped potential under the hood…
Power to Augment existing HTML tags
Let’s say you’re not particularly fond of the idea of adding a <Link> or <ActionLink> Tag to your arsenal because you think the Anchor <a> Tag in HTML is perfectly good for links, but you’d really like the ability to hook that up to the Html.ActionLink() helper built into ASP.NET MVC or the this.LinkTo() helper built into FubuMVC. Why should you have to go and create a new Tag then? Well, with Spark Bindings, you don’t - just put an Anchor Tag into your view like this:
<a action="Register" title="Register">Register</a>
… and the Bindings will take care of the rest. Notice that the “title” attribute is already part of the HTML Spec for the Anchor Tag, and will simply pass straight through the Spark Bindings parser like magic and render just fine on the other side. You’ll also notice that it does not have an “href” attribute but instead, we’ve invented a new “action” attribute which Spark Bindings will feed into the HTML Helper and get the rendered output from that before rendering to the view.
Simple, but remarkably powerful and tidy.
OK, OK! What does this Bindings.xml look like?
Well, firstly we default by convention to looking in the root “Views” folder but this can be overridden if you like – just check the documentation. Then you start by wrapping all your <element> definitions inside a root <bindings> element. This is best demonstrated by example, so here’s the simple binding for the Label Html Helper:
<bindings>
<element name="Label">Html.LabelFor(m => m.@For)</element>
</bindings>
Then you can get a little more complex. Here’s the binding for the CheckBox Html Helper:
<element name="CheckBox">
Html.CheckBoxFor(m => m.@For,
new Dictionary[[string,object]]{{"@*"}})
</element>
What the HECK is that freaky looking Dictionary thingy?! Relax, that’s nothing to do with Spark Bindings, that’s the Dictionary parameter that comes with the Html Helper and Spark is just being a good citizen and allowing you to pass that through to the other side and the “@*” is just a wild-card means of passing the values back out the same as they came in. But you can check the documentation for further details.
Next, let’s have a look at how we managed to simplify the Form Html Helper – that one has kept many an ASP.NET MVC developer awake at night:
<element name="Form">
<start># using (Html.BeginForm("@action", "@controller",
new RouteValueDictionary{{"@route-*"}},
FormMethod.@method,
new Dictionary[[string,object]]{{"@*"}})) {
</start>
<end># }</end>
</element>
There, now aren’t you glad we caught that closing curly brace for you and replaced it with a nice </Form> closing Tag? :)
Power of Overloading
But that’s not all. What if you liked your perfectly good FizzBuzz Helper, and only wanted to change to your new FizzierBuzzing Helper in certain places in your project – well you’ll be happy to hear that multiple overloads are supported for any HTML Tag, which means that all you need to do is create an overload in your Bindings.xml and differentiate the two somehow with a flag or parameter of sorts, and then only add that flag to the parts of the project you’d like to use the second version in.
Here are 4 different Overloads for the standard Anchor <a> Tag and they are ordered from most specific to the least specific in terms of parameters – this is how the Bindings knows which one to pick – it picks the first one where all parameters are satisfied. So you need to put your most specific overload at the top:
<element name="a">
Html.ActionLink("child::*", "@action", "@controller",
new RouteValueDictionary{{"@route-*"}}, new Dictionary[[string,object]]{{"@*"}})
</element>
<element name="a">
Html.ActionLink("child::*", "@action",
new RouteValueDictionary{{"@route-*"}}, new Dictionary[[string,object]]{{"@*"}})
</element>
<element name="a">
Html.ActionLink("@text", "@action", "@controller",
new RouteValueDictionary{{"@route-*"}}, new Dictionary[[string,object]]{{"@*"}})
</element>
<element name="a">
Html.ActionLink("@text", "@action",
new RouteValueDictionary{{"@route-*"}}, new Dictionary[[string,object]]{{"@*"}})
</element>
You’ll probably recognise the “child::*” syntax if you’ve ever worked with XPath queries. This is where it gets its roots and it’s nothing to be afraid of. It is basically indicating to Spark that this Binding element definition is not just an element like a self closing <br/> Tag, but that it will contain child content that needs to be processed as well. This allows you to put Spark code or calls to Macros inside your elements in the view. Something I intend to elaborate on in a deep dive post.
For Html Helpers that return void, simply call them by putting a # up front like this:
<element name="Partial">#Html.RenderPartial("@name", new ViewDataDictionary{{"@*"}});</element>
Can I have multiple Bindings.xml in a project?
Ah, so you’re thinking maybe about supporting themes or perhaps want to have elements render differently in different parts of the same site? Well yes you can. Spark provides an IBindingProvider service that allows you to direct how Bindings are located, and this can be done down to a view by view basis. I’ll have to save that topic for another post though since that’s slightly more in depth and you’ve probably already fallen asleep by now ;) Let me know via the comments though if this is a particularly burning issue for you and I’ll see if I can get it done sooner…
Now you’re just being silly!
I’m sure you’re all going to find new and even more interesting ways to use this feature. I can’t take credit for this one and it’s one of the more, shall we say, creative examples that Lou came up with for his ND2010 talk. It’s basically a text colorizer that uses an Html Helper to build <span> Tags of text across a spectrum of colours between a min and max colour that you specify as parameters to the Helper. The Html Helper looks like this:
public static object Colorize(this HtmlHelper html, string text, Color min, Color max, double value) {
var color = Color.FromArgb(
Range(min.R, max.R, value),
Range(min.G, max.G, value),
Range(min.B, max.B, value));
var tagBuilder = new TagBuilder("span");
tagBuilder.SetInnerText(text);
tagBuilder.MergeAttribute("style", "color:" + ColorTranslator.ToHtml(color) + ";");
return MvcHtmlString.Create(tagBuilder.ToString());
}
private static int Range(int min, int max, double value) {
return min + (int)((max - min) * value);
}
and then we invent a new HTML Tag for use in our view – let’s call it <spread> which maps to our new Html Helper, and we add it to our Bindings.xml like so:
<element name="spread">Html.Colorize("child::*", @min, @max, @value)</element>
So now we make a call to our new Tag in the Spark HTML view like this:
<p>You're kidding right? -->
<var words = "new[]{'O','oo','h',' Lo','ok ','at ','al','l t','he ','pr','et','ty ','co','lo','ur','s'}" />
<spread
min="Color.Green"
max="Color.Red"
each="var x in new[]{.1,.2,.3,.4,.5,.6,.7,.8,.9,.8,.7,.6,.5,.4,.2,.1}"
value="x">${words[xIndex]}
</spread>
</p>
Now what you see above is a little bit naughty – in reality the “words” array will come from your strongly typed view model as something like “Model.Words” and likewise with the array of values, but I put it in the code here so that you can a clear picture of how we got the output you see below. Equally, the “Color.Green” and “Color.Red” values, as a best practice should come from something like “Model.Min” and “Model.Max” so you get the idea right? KEEP THE CODE OUT OF YOUR VIEWS… m’kay?! :)
Doing the right thing presents you with much prettier view code that looks something like this:
<p>You're kidding right? -->
<spread
min="Model.Min"
max="Model.Max"
each="var x in Model.SpreadValues"
value="x">${Model.Words[xIndex]}
</spread>
</p>
And the output…
Nah, that’s just crazy! You wouldn’t actually do that would you? Would YOU?!
In reality, this isn’t necessarily something you’re going to do. What I’m demonstrating here is that you can control your rendering at incredible levels of detail and you can make your rendering subject to functions that you haven’t yet dreamed up! And that because the code is contained in class files behind the scenes, you’re able to control and unit test before they hit a view, and all the view has to do, is call out to those bits, but never has to get involved in the actual thinking.
This my friends is the way MVC views were always meant to be…
Don’t stop now…
I wanted to try and keep this post geared towards beginners or people that just want to get up and running quickly, but I’m not sure I’ve succeeded entirely in that undertaking. It may be worth doing a more in depth post highlighting some of the even more powerful possibilities of this feature, so let me know if that’s something you’d be interested in seeing and I’ll put something together.
But in any case, if you wanted to get the scoop on the syntax and provisions made by the parser for you to extend this feature, then I urge you to have a look at the documentation, set your mind free and start giving your views the makeover they deserve!
Also, if you’re looking for a more complete Bindings sample to play with, you can download it from the slides & samples downloads for the mvcconf talk I did in July.
And before I go, I’d like to say a personal thank you to Lou for creating such an awesome view engine and giving MVC web developers such an amazing experience! This Bindings feature is surely a crowning glory of sorts and I think it gives Spark an edge that really counts. Things are only going to get better…
And finally – look out for Bindings IntelliSense support coming soon to you via SparkSense…
Until next time…
Robert The Grey
Lovely stuff Rob...now go and finish packing for your vacation!
ReplyDeletePortugal here we come! :)
ReplyDeleteAwesome post Rob, really appreciate it! I would definitely be interested in a post on using IBindingProvider for theming, I've got it working but it's hacky as theres no ControllerContext passed in to the IBindingProvider. The potential for this is really huge though, I can support both mobile and desktop browsers with one view just by using some html IoC ;)
ReplyDeleteWorth ponting out maybe that a simple way of inventing new tags can be done with Spark macros also, e.g.:
ReplyDelete!{ Html.TextBox(field, null, new { class = "textbox" }) }
But yes - looking at what you've done, it turns it up to 11!
Great article, I'm looking forward to the deep dive. I could see an application in design time WYSIWYG features (gasp!)
ReplyDeleteDefinitely looks interesting and I'd definitely like to see more elaboration on the topic if you chance to make such postings.
ReplyDeleteShoot, I've been doing this with Partials for awhile. This looks better though.
ReplyDeleteYeah me too! :) But with the power of overloads and multiple bindings on a per ViewFolder basis starts opening all sorts of theming and other possibilities without the need for all sorts of trickery in your Partials :)
ReplyDeleteNow we can use Partials for their intended use - Partial parts of a page :) - but heck of a nice thing that they were able to help us out for so long...
Bindings are the most awesoem thig ever to come to views, great post.
ReplyDelete