Making view templates as dumb as possible

Products and services usually start with a set of user needs which are then translated into screens (and other things) that meet those needs.

The screens are translated into frontend code and while that’s all happening we may try to find the best way to split up the pieces into components.

Once that’s done, it’s time to integrate data and logic into the templates for the user to actually interact with.

The object that contains the data and logic is called a view model — it’s a model designed for the view.

We can’t design a view-model without first understanding the needs of the view. And we can’t understand the needs of the view without first understanding the visual and interaction design requirements etc.

Sometimes, a view model is badly designed or neglected. By that I mean it exposes too much low-level logic which makes templates difficult to work with.

For example, imagine a welcome message that consists of some text and a link. And imagine it renders differently depending on the following circumstances:

  • When the user isn’t signed in it says Welcome — [sign in]
  • When the user is signed in and the the user’s name is known it says Welcome — [sign in]
  • When the user is signed in and the user’s name is unknown it says Welcome back — [sign in]

Consider the following template:


{% if isSignedIn %}
  {% if firstName and lastName %}
    <p>Hello {{firstName}} {{lastName}} - <a href="{{signOutLink.href}}">{{signOutLink.text}}</a></p>
  {% else %}
    <p>Welcome back - <a href="{{signOutLink.href}}">{{signOutLink.text}}</a></p>
  {% endif %}
{% else %}
  <p>Welcome - <a href="{{signInLink.href}}">{{signInLink.text}}</a></p>
{% endif %}

There’s no need for separate first and last name properties because they’re styled the same. And there’s no need to check whether the user is signed in.

See this instead:


<p>{{message}} — <a href=”{{link.href}}”>{{link.text}}</a></p>

The view model has been designed from the outside in. It contains just what the template needs in order to render itself.

As there’s always a message and a link, there’s no need for conditionality.

This template is 1 line compared to the original line and consists mostly of HTML which is far easier to read.

This template is dumb and means complex logic can live somewhere more appropriate. And as the logic lives outside the template, it can be unit tested.

Next, I’ll cover some other common scenarios where we can make our templates dumb.

1. Views shouldn’t know where data comes from

If, for example, your view displays a message of ‘Hello Adam’ the view model should be:

{ message: "Hello Adam" }

The template would be:

<p>{{message}}</p>

The message may consist of text from resource files or the database but the view shouldn’t need to worry about that.

2. Views shouldn’t know why something should be displayed

If, for example, a message is only displayed when the user is signed in, the view model should be:

// view model when signed in
{ showMessage: true, message: "Hello Adam"}

// view model when not signed in
{ showMessage: false }

The template would be:

{% if showMessage %}
  <p>{{message}}</p>
{% endif %}

The template shouldn’t be concerned about why the message is shown or not.

Summary

Without care, templates can become complex.

Templates shouldn’t have to infer display logic from multiple properties and they shouldn’t have to care where its data comes from.

Templates should be easy to read and consist mostly of HTML with just enough data to support the display.

This keeps templates simple and easy to maintain. It also means complex logic can be unit tested and live in files that are better suited to such logic.