Clean views with UIHints
Simple lists of label-value-pairs on information pages often result in cluttered views. Tidy those up drastically with some simple UIHint-magic.
In the web world we're often listing string-properties along with a label on our templates. Typically found on pages that represent single entities that has a predefined set of properties, like employees, stores, departments or products. Often, this results in loads of if-checking to avoid displaying a label that has no following value, which quickly kills the readability of our views. Utilizing the UIHintAttribute
lets us simplify this greatly.
Our model might look something like this:
[ContentType(GUID = "...")]
public class PersonPage : PageData {
public virtual string Name { get; }
public virtual string PhoneNumber { get; }
public virtual string Address { get; }
}
And our display template for this section of the page, for instance PersonInfo.cshtml
might look like this:
@if(Model.Name != null) {
<li>
<span class="label">
@Html.Translate("/some/strings/name")
</span>
@Model.Name
</li>
}
@if(Model.PhoneNumber != null) {
<li>
<span class="label">
@Html.Translate("/some/strings/phonenumber")
</span>
@Model.PhoneNumber
</li>
}
@if(Model.Address != null) {
<li>
<span class="label">
@Html.Translate("/some/strings/address")
</span>
@Model.Address
</li>
}
Ugly, hard to maintain, error prone, wastes space, repetitive. It just looks wrong.
What if your your view could look like below?
@Html.DisplayFor(x => x.Name)
@Html.DisplayFor(x => x.PhoneNumber)
@Html.DisplayFor(x => x.Address)
Annotate your properties with a nice UIHint, like so:
[ContentType]
public class PersonPage : PageData {
[UIHint("MyFieldType")]
public virtual string Name { get; }
[UIHint("MyFieldType")]
public virtual string PhoneNumber { get; }
[UIHint("MyFieldType")]
public virtual string Address { get; }
}
Then create a display template (by default, put it in Views/Shared/DisplayTemplates/MyFieldType.cshtml
- note that the file name equals the UIHint), like so:
@model string;
@if(Model != null) {
return;
}
@{
var propertyName = ViewData.TemplateInfo.HtmlFieldPrefix;
}
<li>
<span class="label">
@Html.Translate(string.Format("/contenttypes/icontentdata/properties/{0}/caption", propertyName))
</span>
@Model
</li>
Clean, easy to maintain, easy to debug, no wasted space, component reuse.
A big fat bonus is that the translated label on the page will be the same as what the editors see inside Episerver edit mode - giving consistency, ease of use for editors and forcing you to actually translate it properly.
Note: The property name we retrieve in the view will represent anything after the x
in the DisplayFor
expression, meaning that DisplayFor(x => x.CurrentPage.Address)
will not work, as the propertyName will be CurrentPage.Address
. Move your properties into a separate partial view, which gets x.CurrentPage
as the model.
This is really just using built in functionality in ASP.NET/Razor, but it often seems forgotten, so I thought I'd throw this out there.