Not content with ruining JavaScript, it seems you’re set on ruining HTML too.

If you ask people what they’re building their client-side web apps in today; chances are they’ll tell you some hipster JavaScript framework that could be found listed on the TodoMVC website. The most popular ones tend to be Angular, Ember, Knockout, Backbone and more recently, Polymer.

Most of these frameworks have this “great” feature that lets you “easily” reference data in your views using binding expressions. They look something like this:

<!-- Knockout -->
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>

<!-- Angular -->
<ul class="phones">
	<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
		<span>{{phone.name}}</span>
		<p>{{phone.snippet}}</p>
	</li>
</ul>

<!-- Ember -->
<div>
	<label>Name:</label>
	{{input type="text" value=name placeholder="Enter your name"}}
</div>
<div class="text">
	<h1>My name is {{name}} and I want to learn Ember!</h1>
</div>

Although the syntax varies slightly; they’re all doing similar things. They give the ability to embed some expression into your HTML that is “magically processed” by the framework. This means no manual DOM manipulation; you can write nicely formed (ish) HTML inside these expressions and the framework will do all the mundane tasks like creating nodes, wiring up event handlers and updating the DOM when your model changes. This all sounds like a nice convenience, it’s not hard to see why everyone is jumping on the bandwagon and developing this way.

However; turns out that it’s not all as advertised; there are some significant flaws in this approach that often go unnoticed until you’re trying to do something complicated.

Where I work, we’re currently building some prototypes in many of these new technologies to help guide our decision on our technologies for new versions of our products. It seems that each dev using one of these fancy frameworks is struggling and we spend a lot of time debugging issues or scouring StackOverflow for how to do (what we’d consider) basic tasks.

Some of the frustrations include…

No help from your tools

Because these binding expressions aren’t real HTML constructs, you’ll get no help from your tooling with them. No highlighting; no code-completion; no tooltips; no static analysis or type-checking. As far as your HTML editor is concerned, these expressions are just strings.

Simple tags are never simple

You’ll start off binding properties like {{ name }}, but you’ll soon find your expressions growing into complicated messes. You’ll find the built-in functionality doesn’t quite do what you need and you’ll start having to bend the framework with crap like this, writing a JavaScript function that returns a function simply to apply a filter with a dynamic name:

<tr
	ng-show="banners"
	ng-repeat="banner in banners.data"
>
	<td ng-repeat="col in banner">
		{{ col | dynamicFilter : banners.cols[$index].formatter }}
	</td>
</tr>
	
<script>
	myApp.filter('dynamicFilter', function ($filter) {
		return function (value, filterName) {
			if (Formatters[filterName])
				return Formatters[filterName](value);
			else
				return value;
		};
	});
</script>

Nothing ever works first time

As your binding expressions get more complicated, you’ll find that they rarely work first time. You’ll end up playing the shotgun-debugging game of tweaking your expression and reloading to see if you got it right (this will really suck if you have a long cycle to get back into the correct position for testing; such as having to manually log in to your app). For example, in Knockout, observables are functions, so when you want values you often need to add parens to the property. However sometimes your properties are not observable, so sometimes you don’t.

<!-- Which ones need parens()? -->
<input data-bind="value: filterData.DateCriteria().DateFrom" type="text" id="DateFrom" />

Error messages rarely exist and are frequently useless

When you get your binding expressions wrong; you’ll frequently see nothing. If you’re lucky, you might get a JavaScript error. If you do, it’ll probably say something like e is not a function and give you a stack trace that’s a hundred frames deep inside your framework and no part of it points at your HTML binding expression.

Debugging is near impossible

You can’t put a breakpoint on a {{ stupid binding expression }} because it’s not JavaScript (or is it?). You’ll often end up pulling them out into “computed properties” or some equivalent, but now your JavaScript is filling up with extension functions on objects that make no sense outside of framework-specific view-funkiness. All because your framework has a half-arsed parsing of JavaScript expressions for bindings.

What’s the alternative?

Simple. Do your programming in a programming language. Don’t try to embed it in some crazy will-never-be-a-standard binding expression invented by a fly-by-night JavaScript framework.

You probably don’t want to go back to completely manipulating the DOM by hand, but there are options like React (without JSX, since JSX sucks for many of the same reasons above) which avoids DOM manipulation (instead favouring the idea of just describing the “current state”) and keeps all your code as code. This way, your existing tools, linters, coverage etc. will all work fine. Want to extract a function to reuse it? No problem! Want to refactor->rename? No problem (well, ish… as well as this works in JS!). Want to use TypeScript and have type-checking in your expressions? No problem (well, ish… when TypeScript+React play nicer together ;)).

At work, one of our devs is building a prototype being built in React, and I’m also building one in Dart (in a React-inspired, code-generating elements way, while I quietly hope Dart TagTree evolves into a usable library). Neither of these prototypes are hitting the same frustrations as those that encourage {{ silly binding expressions }} in HTML.

HTML is a declarative markup language. Let’s leave it as such, and stop trying to crowbar non-standard binding expressions into it.