Template engines have seen a recent rise in use with the popularization of the MV* model of programming. It's easy to see why, we are no longer writing websites where content is key, but creating dynamic applications where user interaction decides what is on screen. Because of this we don't want to download html from the server every time, instead we want to take our data and put it in a template that we can reuse for the layout. It's an obvious choice and one that works well. However we've started to run in to a bit of a problem, but first a recap on some HTML history:
Event Handlers in HTML
Not so long ago we were writing things like this:
<a href='#' onClick='window.open("http://rhysbrettbowen.com");'>open site</a>
okay, not a good example but that's not the point. The point was that we were littering our HTML with code that only run on an interaction with the element. The issue wasn't the element interaction, it was where we put the actual logic. The issue is that putting actual implementation logic in the HTML gave us a whole other place to hunt for bugs and find where logic was, plus it looked ugly. Sometimes the code you wanted to write had to be more than a few lines long, and things really got bad. It also meant that any change to the implementation logic would have to be changed in the HTML, and changing the HTML may change the behaviour of the page so it meant less people could alter it.
Event Handlers in Scripts
So then we put our event handlers in the scripts. This was great and solved all sorts of problems. It meant that we could get a handle to an element and attach functionality there. This meant we could also change the functionality based on different circumstances. This worked great, while we knew what HTML was on the page. Then we started to get clever and made our pages dynamic.
Event Handlers in Templates
So we started using templates which could have data passed in to them. This meant we had the power to inject data directly in to the DOM before adding it to the page. We also put in functionality in things like data attributes that would declare what functionality an element should have, and all this is a good thing. (Also please note the difference in declaring the functionality, we now declare something on elements but the actual implementation lives in libraries like Knockout or Angular).
Back to the issue at hand
The problem though is that with the ease of using templates we forgot about the bad old days and reintroduced logic in to the template. By logic I mean things like "IF" statements. The issue with IF statements is that they increase code complexity, and they're doing it in the template which is a place that we would like people like designers and other non-professional coders to write. Every time you nest and IF statement you increase the factor of code paths through your code by 2. So if you have 3 IF statements then you suddenly have 2 ^ 3, or 8 different paths your code can take. Because it's logic you should then be writing 8 different tests for your template to get 100% coverage. Do you really want to do that?
So how do we fix it?
The fix is usually quite easy. In most cases you can compute a value and pass it in to the template. This works for things like adding on a class depending on a value. You can use things like rivetsjs to declare your intent in the data attributes in a manner easy enough for a non-professional coder altering the template to understand and use (DSLs like this are great for a while team to learn so they can collaborate together).
In pure OOP there are no IF statements - there is even a website about it. Basically instead of an if statement with two paths you have two different objects. These object share a function name and dependent on which object you call a that function will run. In the case of MV* we've already got objects that come out as DOM, and that is our views (or controls dependent on how you look at it). So all you need to do is write your top view and pass in to it the subview that should be shown in that spot, now dependent on which subview you pass in you will have different things appear in the dom (as they themselves have different templates). The disadvantage to this is no you have a view hierarchy, but if you already have a view manager this should be easy to use. The advantages though are great, you've removed complexity from the templates and what's more you've also broken out the templates in to more logical pieces. Now you can work on those subview templates individually (and I bet they'll increase in complexity over time so you'll be saving yourself a headache).
The other great thing is now there is no logic in the template you shouldn't need to run it any time there is a change in the data. A problem is an if statement may not put some elements in the page, that you'll want to show on the data change, so you would have to re-run the entire template. If you've split out in to a smaller view, you only have to replace the view and the rest of the template can remain untouched. That's a big win not having to re-render DOM and reset all event handlers. Also because you no longer have to re-render the entire DOM you can now use bindings between elements, as those elements won't change.
So just say no to logic in templates.
Very informative article. I'm working on a site now that where tons of elements are bound to jQuery events and it's getting too complicated.ReplyDelete