Skip to content
RobertTheGrey edited this page Jan 13, 2013 · 1 revision

About bindings

A binding is a small rule that matches an element in a spark file and provides a code fragment that takes its place in the generated class.

Adding bindings to a project

By default bindings are loaded from a ~/Views/Bindings.xml file if it is present in your project. There are two main forms of bindings,

<bindings>
  <element name="TheElementName">TheReplacementCode</element>
</bindings>
<bindings>
  <element name="TheElementName">
    <start>StartElementReplacementCode</start>
    <end>EndElementReplacementCode</end>
  </element>
</bindings>

It is loaded through the IViewFolder abstraction so it does not need to be a physical file if that path is provided by another source.

You may also replace the default IBindingProvider service on SparkViewEngine instance if you want to entirely replace how binding definitions are located and parsed.

Simple code replacement of an element

By default a binding will match an element by name, and the replacement code will be used as an output expression.

<bindings>
  <element name="Greetings">"Hello, " + Model.CurrentUser.Name + "!"</element>
</bindings>
<!-- using the binding -->
<p><Greetings/></p>
<!-- is the same as -->
<p>${"Hello, " + Model.CurrentUser.Name + "!"}</p>

Using attributes as parameters

The replacement code may contain small terms that are designed to resemble xpath selectors. If they are present it has two effects: the rule will only match elements in the spark template which have those attributes, and the value of the attribute will replace the term in the generated code.

<bindings>
  <element name="Greetings">"Hello, " + @username + "!"</element>
</bindings>
<!-- using the binding -->
<p><Greetings username="Product.Name" /></p>
<!-- is the same as -->
<p>${"Hello, " + Product.Name + "!"}</p>

Possible matches for bound elements are evaluated in order of appearance from the binding provider. The first one that satisfies all requirements is used, so to overload an element put the most specific bindings first.

<bindings>
  <element name="Greetings">"Hello, " + @username + "!"</element>
  <element name="Greetings">"Hello, " + Model.CurrentUser.Name + "!"</element>
</bindings>
<!-- using the binding -->
<p><Greetings/></p>
<p><Greetings username="Product.Name" /></p>
<!-- is the same as -->
<p>${"Hello, " + Model.CurrentUser.Name + "!"}</p>
<p>${"Hello, " + Product.Name + "!"}</p>

Treating attribute values as text by default

There are times when an attribute containing code is given a string value, and the nested quotes seems counter-intuitive. In the following example you wouldn't assume the quotes around the action and text are needed.

<bindings>
  <element name="ActionLink">Html.ActionLink(@text, @action)</element>
</bindings>
<p>Please <ActionLink action="'Register'" text="'register'"/> to continue.</p>

To make the quotes implied, wrap the attribute in the reference with single- or double-quotes. The attribute values in the spark templates will be treated as text, and may contain ${expr} to mix anything dynamic into the generated code.

<bindings>
  <element name="ActionLink">Html.ActionLink('@text', '@action')</element>
</bindings>
<p>Please <ActionLink action="Register" text="register before ${DateTime.Now.AddSeconds(3)}"/> to continue.</p>

Binding elements that are already meaningful

If an element name has bindings, but it does not match any of the rules because of missing attributes, then it will be output as if it was plain markup like any other element in the temples. That can be useful to give special meaning to existing elements, like <a>, if it contains some meaningful attributes you define, like action="...".

<bindings>
  <element name="a">Html.ActionLink('@text', '@action')</element>
</bindings>
<p>Please <a action="Register" text="register before ${DateTime.Now.AddSeconds(3)}"/> to continue.</p>

Using child text as a value

If you want use a binding element around some child content, and that child content must be passed to a macro or html helper as an argument, you can refer to it with a 'child::*' term.

<bindings>
  <element name="a">Html.ActionLink('child::*', '@action')</element>
</bindings>
<!-- normal usage of a link, followed by an action="" attribute to use the html helper  -->
<p>
  Either <a href="~/">return to the home page</a> 
  or <a action="Register">register before ${DateTime.Now.AddSeconds(3)}</a>
  to continue.
</p>

Initializing dictionary with wildcard attributes

Html helpers often take a dictionary of name/value pairs to do things like add route values, or additional html attributes, to the output they produce. An attribute wildcard syntax {{'@*'}} may be used to expand any unused attributes into a new dictionary.

<bindings>
  <element name="a">Html.ActionLink('child::*', '@action', '@controller', 
    new Dictionary&lt;string,object&gt;{{'@*'}})</element>
</bindings>
<p>Please <a action="Register" controller="Account" title="This is the hover text">register</a> to continue.</p>

Because @action and @controller are in the replacement code they will not appear in the dictionary. The attribute value may also contain code in ${expr} syntax.

Initializing several dictionaries with prefixes

There are cases where a helper may take several dictionaries. One example would be route values in addition to html attributes. In that case a prefix may be used in the wildcard attribute reference.

<bindings>
  <element name="a">Html.ActionLink('child::*', '@action', 
    new RouteValueDictionary{{'@route-*'}}, 
    new Dictionary&lt;string,object&gt;{{"@*"}})
  </element> 
</bindings>
<ul>
  <li each="var product in Products">
    Id ${product.Id}: 
    <a action="show" route-id="${product.Id}" title="${product.Details}">${product.Name}</a>
  </li>
</ul>

Initializing objects and anonymous objects

If a wildcard element is not surrounded with a double-curley brace, then the code generated will be suitable for object initialization.

<bindings>
  <!-- element Foo will call function Foo() -->
  <element name="Foo">Foo(new Thing{'@*'})</element>
</bindings>
<!-- macro puts Foo() in scope. assumes class Thing exists. -->
<macro name="Foo" theThing="Thing">
  <span>Thing ${theThing.Id}: #{theThing.Name}</span>
</macro>
 
<p>  
  <Foo Id="${34}" Name="Frank"/>
  <!-- is the same as -->
  ${Foo(new Thing {Id = 34, Name="Frank" })}
</p>
</bindings>

No return value

Some helpers that write directly to the response and have a void return value. If you put a hash at the front of a binding it will simply generate the code as a statement and omit the output.

<bindings>
  <element name="Partial">#Html.RenderPartial("@name", new ViewDataDictionary{{"@*"}});</element>
</bindings>
<Partial name="ShowThis" Foo="My Caption" Bar="${Model.Product}" />

Wrapping child text with two code replacements

Another way to use a binding as a element with child contents is by providing two replacement code fragments. In this case the child content is output normally - it is not marshalled into a parameter - the start and end code is simply inserted at the point where the start and end element appeared.

<bindings>
  <element name="Form">
    <start># using (Html.BeginForm("@action", "@controller", new RouteValueDictionary{{"@route-*"}}, FormMethod.@method, new Dictionary&lt;string,object&gt;{{"@*"}})) {</start>
    <end># }</end>
  </element>
</bindings>
<div>
  <Form action="Create" controller="Product">
    <!-- input elements and whatnot -->
  </Form>
</div>