Declarative Templates: A better JavaScript View Layer

Julien Etienne
6 min readMay 20, 2017

--

Yes there is relevance

Templating in JavaScript is messy because semantics are tangled with logic and apparently that’s ok. Every HTML templating solution I’ve come across struggles with clutter:

  • Mustache
  • Handelbars
  • JSX
  • Underscore
  • Angular
  • VueJS
  • Embra
  • Aurelia

Analogy time: Mechanical schematics share similar qualities to HTML semantics. They describe the layout, the content of parts, how they fit together and a little meta data.

Where things are

Imagine detailed schematics of an engine, with each moving part being represented in various states (since we don’t have conditions and loops in diagrams) e.g., a complete diagram of an exposed engine but with piston head illustrations at each stroke position as well as common states of the crank shaft, etc. It would be a bit of a useless mess, practically it’s common for these states to be represented in separate motion diagrams:

How a thing operates

In front-end engineering we can use this common sense, we can separate where things are from how they operate. We just don’t, at the moment, it’s a phase ;-)

Presentational + Container | Smart + Dumb | Fat + Skinny doesn't solve the problem

This is not a barrage of insults, if you are not aware these are common terms for structuring components in React. They’re all the same thing and they do tend to create some separation so let’s give credit where due. But it is not a clear separation of logic and presentation, the dumb side may not be thinking much but the smart side is still painting a lot of the picture. To move forward we have to go beyond the literal and conceptual constraints of libraries like React and JSX.

The Indisputable Reality Of Logic Within Semantics

Imagine a fruit bowl containing a wide variety of different fruits all jumbled randomly. Can we identify all citrus fruits? Yea so what’s big deal!

We hire two halls, hall A has 300 fruit bowls as mentioned and hall B is equivalent except fruits of the same species are placed together. Despite our different cognitive and scavenging abilities…

It is indisputable that elements of an “organised” system are easier to identify compared to elements of the equivalent unorganised or erratic system.

What is meant by “organised” is up for interpretation, maybe organising by species (orange/ lemon/ etc) is not as effective as organising by ancestral species (Hesperidium. Berry^ Pome). Notice I just used very strange delimiters, but they are still delimiting. Without context you could likely Identify these three words hesperidiumberrypome but the camel case “convention” makes it easier to read HesperidiumBerryPome.

The disadvantage is not obvious with a single fruit bowl or a script file, it’s when you have dozens, hundreds or maybe thousands of fruit bowls or script files, despite your super powers it will take you a little longer to mentally seek and separate the information you are looking for.

Declarative Templates To The Rescue

** Update: The examples below use functions as tags, but the concept of separating logic has nothing to do with the markup format providing it is pure valid JavaScript. Template literals would make a better choice IMO **

Declarative templating is:

  • Emulating a markup language within JavaScript using first-class functions in the exact syntactical notation of the markup language being emulated. That last part is crucial, why should you have to learn a new system?
  • Having the ability to pass first class functions down into the templates as arguments in a declarative manner by name.
  • Using the discipline and convention of avoiding the use of JavaScript APIs, direct functions, statements, loops or evaluations.

Same notation !== same syntax. Being obsessive with syntax perfection is where the benefits of this system come to a halt. The purpose is not to make it look perfectly like HTML or XML, the purpose is to create something that:

  • Has strong readability that correlates to the native language.
  • Can promote the separation of presentation and logic
  • Is not an complete abomination

It’s not only pleasantly readable but powerful, as you don’t “need” to use an array to define children. Exact notation means that anyone who understands for instance HTML will understand exactly what’s going on.

Bonus! The spread operator …childElements means you don’t need to wrap each set of components within a div wrapper as React insist. Also properties are not reserved words so {class: ‘is-totally-fine’}

Separation Of Concerns

I lied, there’s a few more principals to this simplistic methodology. Each UI component should contain a view and controller file or directory separation. Here’s an ./src/js/ui/todo-item/ example containing:

  • view.js
  • controller.js

The pointless return statement is just so you can see what’s going on. But notice:

  • There are no switch, if, loops, map functions or any API functions within view.js
  • The controller can contain elements or import from a smaller view file as a part to be manipulated but it will not contain HTML like containment no matter how small they are.
  • The main view can import other view files but not with logic, only to insert arguments.

The controller imports the view as a function and passes resolved arguments, those arguments can be loops, events, conditional etc, it doesn’t matter. This is a discipline not a framework feature.

The component is then imported and rendered to the main rendering script with changes. This is real world separation of the view and view logic. This is important for CSS developers who want to focus solely on the semantics and not a bunch of if statements, loops and weird objects. A CSS developer should find most of what they need within the view files and for the rest, because the JavaScript developer uses declarative names, they can easily find the class names of let’s say loops and conditions in the corresponding controller without understanding how it all works.

tl;dr

So let’s recap because this is far easier, cleaner and feasible than the long winded theory:

  • Structure: View & Controller only.
  • View: Tags are first class functions.
  • View: The notation of attributes and child nodes/ elements are in respect to the markup language (HTML/ XML/ Whatever).
  • View: Arguments as values are passed into the view template from the controller.
  • View: Strictly no logic is allowed which means no statements, conditions, loops, JavaScript methods, expressions etc.
  • View: Arguments are declarative values or functions that can accept other arguments.
  • View: The view can import other view files as a value or function.
  • Controller: The controller contains all the logic: Conditions, loops, JS methods and everything you can not place in the view template.
  • Controller: Within the UI directory the controller imports its view layer and is the only part in the app responsible for executing its view.
  • Controller: Event logic is managed within the controller and passed into the view.
  • Controller: Can contain single tags but if a tag contains children it needs to be put into a separate view file.
  • Structure: The folder of the UI component e.g., /footer/ must contain and can only contain ./controller.js ./view.js and or /controller/* /view/*. This is a clean separation of concerns. If it’s not logic it’s view, if it’s not view it’s logic. Many view files requires a folder, many controller files require a folder of course.

In my view + controller example code snippets, the controller is imported to be patched and render by the virtual-dom with whatever arguments are necessary.

I apologise for the hype as I have no demos online. I have toyed with a todoMVC app and a partially-scientific calculator using this methodology. You can checkout hypertext for an idea of creating the syntax but I wouldn’t advise using it as it was build for virtual-dom which is now discontinued.

I really hope this has been a tiny bit inspiring and I welcome any opposing opinions as it defiantly helps the entire JS templating debate.

Thanks for the read ;-)

--

--