Critique
Over the years I have looked at Java Swing, SWT, Flex, GWT, jQuery, SmartClient, JSP, Wicket, Struts, Spring MVC and now AngularJS, in my long search for the "perfect" GUI toolkit for web apps. I hoped AngularJS would be the end of my search, but right from the beginning something in AngularJS smelled wrong to me. It took me some time before I was able to put my finger on just what it is I don't like.
Don't get me wrong. You can make web apps just fine with AngularJS. There are just some aspects I don't like - choices I don't agree with. These are:
- The AngularJS directives are a suboptimal choice for HTML generation.
- The 2-way data binding is not always that useful.
Each of these aspects are discussed below.
AngularJS Directives And The Declarative / Imperative Paradigm Mismatch
My main point of critique is against the AngularJS HTML templating style including AngularJS directives. These are a core part of AngularJS so you cannot really choose not to use them.
AngularJS uses the popular {{ }}
HTML templating style also found in frameworks like Knockout and Handlebars. Here is an example:
<ol> <li ng-repeat="theItem in myData.getItems()">{{theItem.text}}</li> </ol>
While this style works just fine for the simple cases, it works less well for more advanced cases.
In web apps the code has to insert data into the HTML page somehow. You can generate the whole HTML page using an imperative language, like Java (on the server) or JavaScript (in the browser). Or, you can try to "teach HTML new tricks" so HTML becomes able to do some of the things you can do in an imperative language. Or, you find a middle ground in which the more static parts of the HTML page is defined using HTML syntax, and the more advanced parts are created using a imperative language. HTML is a declarative language, so another way to put it is, that either you mix a bit of the imperative into the declarative, or a bit of the declarative into the imperative. I will get back to this again later.
In my opinion generating HTML on the client cases works better than generating HTML on the server in most cases. It results in cleaner HTML generation code, and cleaner backend code too. This means, that the imperative language should run in the browser. In AngularJS that language is JavaScript and that is just fine.
Using declarative HTML templates with a bit of data binding injected is not a new idea. It has been tried before in JSP (JavaServer Pages). Here is an example:
<span><%=myObject.getMyProperty()%></span>
In AngularJS it would look like this:
<span>{{myObject.getMyProperty()}}</span>
This data binding injection style is incredibly simple for the simple cases, and thus immediately appeals to most developers. However, as soon as your web app gets more advanced you realize that you need some kind of imperative control logic to direct the HTML generation. Two imperative constructs you will soon need is looping and branching (if-statements). Here is how looping looks in JSP in the first versions:
<% for(int i=0; i < list.size(); i++) { MyObject myObject = (MyObject) list.get(i); %> <span><%=myObject.getMyProperty()%></span> <% } %>
Notice how the <% %> markers switches between Java and HTML. While this allowed imperative control logic to be mixed with the declarative HTML, developers soon realized that mixing Java directly into the HTML resulted in really messy code when the HTML pages got more advanced. The syntaxes of Java and HTML just didn't complement each other very well.
To solve that problem the JSP standard added a concept called "Tag Libraries". Tag libraries used an XML syntax to allow imperative control to be mixed with the HTML. Early frameworks like Struts utilized this to create tag libraries for data binding, iteration, branches etc. Here is an early iteration example:
<logic:iterate id="theBean" name="beans"> <span><bean:write name="theBean" property="theProperty"/></span> </logic:iterate>
This way the syntax of the imperative control logic complements HTML better. However, you run into other problems instead. First, it is not so easy to express advanced constructs like recursive iteration of tree or graph data structures. Second, your HTML template gets messy when you insert too much imperative logic into it. Third, the XML syntax does not support imperative control structures so well. Expressing them in XML is more clumsy and verbose than using a syntax specifically designed for that purpose.
It did not end so well for JSP. The Java community learned that there were better approaches. First were called "Model 2" (early Struts and Spring MVC), and later "Component Oriented Frameworks" (Wicket and JSF - JavaServer Faces). The last time I checked I could not even find JSP in Oracle' official Java Enterprise Edition tutorial.
It is easy to ascribe the demise of JSP to the fact that it was generating HTML on the server. However, that is only part of the problems with JSP.
The Declarative / Imperative Paradigm Mismatch
Declarative languages (such as HTML and XML) are good a modeling state such as documents, or the overall composition of a GUI. Imperative languages (such as JavaScript, Java, C etc.) are good a modeling operations. The syntaxes of both declarative and imperative languages were designed to support their specific purposes. Therefore, when you try to use a declarative language to mimic imperative mechanisms it may work for simple cases, but as the cases get more advanced the code tend to become clumsy. The syntax just wasn't intended for that purpose. Granted, you can create all kinds of workarounds to make it look nicer, but that doesn't change the underlying fact, that you are trying to use a declarative language for imperative operations.
The same is true the other way around. Using an imperative language will often also get clumsy. Anyone who has tried writing an HTML document using JavaScript's document.write()
function, or wired up a Java Swing GUI using Java, or an SWT GUI for that matter, knows how clumsy this code can get.
When you try to use a declarative language for imperative purposes, or an imperative language to for declarative purposes, you end up with what I call "The declarative / imperative paradigm mismatch". This mismatch often results in clumsy or ugly code.
It is easy to think that the declarative / imperative mismatch can be avoided with a programming language that is designed to handle both paradigms. But the problem goes deeper than the language syntax.
It is not the syntax of a programming language that decides if the language is declarative or imperative. It is the semantic meaning of what the language describes that decides between declarative and imperative. You could invent a more Java-like syntax to describe HTML elements. Or a more HTML-like syntax to describe Java instructions. But such syntaxes would not change the fact that one language describes state (HTML documents) and the other language describe commands (operations). Thus, a Java-like syntax for describing HTML documents would still be declarative, and a more HTML-like syntax for describing Java-operations would still be imperative.
Think about it for just a moment. JavaScript actually already has a declarative mechanism built in for defining JavaScript objects. It's JSON - JavaScript Object Notation. You could actually mimic HTML using JSON. Here is an example:
{ html : { head : { title : "JSON mimicking HTML" }, body : { text : "Hello World" } } }
Of course this doesn't mimic HTML attributes or even the combination of text and HTML elmeents possible in the body of an HTML element. But I am sure the JSON syntax could have been extended to cover that.
Imagine you had to generate a JSON object using JavaScript - by mixing JavaScript instructions in-between the JSON notation. Regardless of the JavaScript-like syntax, it would be a hard problem to solve elegantly.
Now we are down to what the declarative / imperative paradigm mismatch is really about. It's just hard to design one language that can both describe state and support dynamic generation of state intertwined, regardless of the syntax. JSP is a good example of that. The above JavaScript / JSON example is a good example of that.
XSL is another fine example of the the declarative / imperative mismatch. XSL attempted to be a declarative language for describing XML document transformations. Again, it worked beautifully for simple transformations, but once you got to more advanced cases, XSL broke own. You had to resort to an imperative language for those parts.
AngularJS is using a similar approach with its directives. AngularJS directives are either HTML elements, attributes, CSS classes or HTML comments which trigger imperative actions in their respective JavaScript implementations. AngularJS directives are similar to JSP tag libraries and are thus prone to the same problems. I find it puzzling that some developers (in the Java community at least) wrinkle their noses at JSP but praise AngularJS. Despite the amount of rational thinking our jobs require, the IT industry is far from always behaving rationally - but that is another topic for another article.
Finding the Declarative / Imperative Balance
There are definitely parts of a GUI which it makes sense to define using a markup language. Markup languages are typically declarative, and declarative languages are good at modeling state. The composition of a GUI is state. Thus, it makes sense to model the overall composition of a GUI using a declarative language (e.g. HTML).
But there are also parts of a GUI (e.g. advanced GUI components) that have too complex a behaviour to be sensibly defined using a declarative language like HTML. For instance, an advanced data grid, or a tree view, or interactive graphs, or animations, or a game. HTML is not very good at representing recursive behaviour, which makes tree structures harder to implement. Granted, you can get around this by reducing the tree structure to a flat list with an indentation level for each item in the list. But the point is that you have to resort to workarounds to get that sort of stuff to work. HTML just wasn't designed for imperative operations.
This diagram illustrates how a modern HTML5 / CSS3 / JavaScript GUI may contain both simple and complex components, some of which are best defined using HTML, and some of which are best rendered via JavaScript.
Finding the right balance between how much is done declaratively and how much is done imperatively is not easy. Somehow we have to bridge or combine these two paradigms. But where on the axis is the optimal balance?
As mentioned earlier I have worked with several different GUI toolkits. In my opinion what has worked best is when you use a declarative approach for the overall composition of the GUI, and use an imperative approach when rendering the more complex GUI components. Look at this HTML template:
<html> <head> </head> <body> <div> <div>My App</div> <div><table id="theTable" jqc-type="jqcTable"></table></div> </div> </body> </html>
It contains a label (My App
) which is easily declared and positioned using HTML. And it contains a data table which is a more complex GUI component, so only its location in the GUI is declared in the HTML. How it is rendered, bound to data and how it behaves when the user clicks rows etc. is implemented in JavaScript.
This model contains a clear separation between the declarative and imperative paradigm. At least in the HTML template part. The implementations of the various components like the data table will be more muddy. But at least that is shielded from the developer making the HTML template. You are not tempted to specify part of it using HTML, and part of it using HTML-like directives. And you do not have to specify the whole GUI composition imperatively either.
The above balance is how Flex works. You can specify the overall composition of a GUI in Flex using an XML format. You can then implement components in ActionScript which can be referenced in the XML. This isn't perfect, but at my current level of insight into GUI technologies, I feel this approach is most optimal I have seen.
In my opinion AngularJS has positioned itself a step closer to the declarative end point than Flex. Yes, you can implement more coarse grained directives in AngularJS so you end up with an HTML template that is similar to the above. But that was never the selling point of AngularJS. It is usually the more fine grained directives that "Angularistas" praise.
After realizing that AngularJS was not what I was looking for, I actually started implementing my own GUI toolkit inspired partly Flex, and based on JavaScript and jQuery. I will get back to that later in this article.
HTML5 Canvas & WebGL
If you plan to render part of your GUI using HTML5 canvas or WebGL, you will also not get much help from AngularJS. Both HTML5 canvas and WebGL have imperative interfaces. The only HTML you see is the <canvas>
element they both use to draw on. In fact, you will end up with a separation between HTML and JavaScript which is very similar to the separation I described earlier, where the HTML only contains a reference to where the components are located in the GUI, and not how they are rendered.
The Intrusiveness of AngularJS Directives
As already mentioned earlier, AngularJS directives are intrusive on your HTML template, just like JSP tag libraries etc. were. In order to "teach HTML new tricks" you end up with HTML full of non-HTML elements and attributes. Think about it for a while. When you need to teach HTML new tricks, that is a pretty good sign it is time to change to the imperative paradigm (JavaScript), except perhaps for very simple tricks.
Another example of how the templating mechanism is intrusive is when you look at how many function calls that can (and will) be embedded in the HTML. Function calls on the $scope
object. Before AngularJS it was "best practice" to keep function calls out of the HTML. For instance, you should not use the onclick
event attributes on HTML elements, but rather attach event listeners via JavaScript. Somehow that was forgotten with AngularJS, and now we are back to embedding JavaScript function calls in the HTML.
Designers Are Not Developers
One argument I have seen often in favor of more advanced HTML templating systems is, that when no programming is involved in specifying the template, then designers can implement the HTML templates independently from the developers. If a designer knows HTML, then he or she will also be able to use the HTML templating system.
I encountered this argument the first time related to JSP and its tag libraries. If a designer learned the tag libraries he or she could implement functionality without having to bother a developer. Adobe Dreamweaver - a popular HTML design tool - even has built-in support for JSP tag libraries. I have seen the same argument on blogs used in favor of the AngularJS HTML template system.
I haven't actually worked in a place where this actually worked. Designers did not implement functionality, even with HTML-like tag libraries or directives. If this works where you are employed, I'd be happy to hear about your experiences.
In my opinion the argument is based on a false premise. The premise that if a designer knows the HTML syntax then they can all of a sudden perform programming tasks, given an HTML-like templating system. While this is theoretically true, the premise mistakes syntax for semantics.
A designer is primarily concerned with looks while a developer is primarily concerned with behaviour
. If you give a designer an HTML-like syntax and asking her to add behaviour to the web page, there is a pretty good chance that she will think "but what does that have to do with design?". It is not what her mind is trained to do, regardless of the syntax.
The same is true the other way around. A developer does not all of a sudden become a designer just because they get a cool graphical API at their disposal. Looks and behaviour are different semantic areas of an application. These semantics are what separates the designer's work from the developer's - not syntax or tools.
There is a significant difference between specifying the looks and behaviour
of an application, even if the syntax used for the two tasks is the same.
AngularJS Without Directives?
AngularJS directives are one of the core features of AngularJS. Take them out, and what do you really have left? The whole framework revolves around them and their binding to the $scope
object, the way AngularJS is designed now. Thus, if you choose to use AngularJS in your project, the directives is a phenomenon you will have to live with.
Two-way Data Binding
Another feature that seem to impress people about AngularJS (apart from its directives), is the two-way data binding between form fields and model properties. However, in a real life application this is not always that useful.
AngularJS will copy the values of form fields into your model object automatically, if the form field values are valid. If a form field is not valid, that model field is left empty. That means, that if you bind your form fields to an object in an array which you are using to display e.g. a data table with, those fields are cleared in the data table too. Or at least in your data model.
You often don't want that. You only want data to be copied from form fields if they represent a valid coherent collection of data. In other words, you don't want any data copied into your original data unless all data in the form is valid. And you don't want any data copied into your model if the user ends up pressing the "cancel" button.
Second, you will often not even want the data copied directly into your data objects until the data has been sent to the server (typically using a remove create or update service), and the server has responded that the data was accepted. Then, and only then do you want your data model on the client updated to reflect the new values. The following diagram illustrates this data flow:
The two-way data binding in AngularJS kind of gets in the way of creating this data flow. You will end up binding your form fields to model variables that are separate from your model data objects, to avoid polluting your data objects. That way you copy data from a model object into form field model properties, further into form fields, then back into form field model properties, then send them to the server, and if that succeeds, you can update your original model data object. Here is the data flow you will end up with in AngularJS:
While you can get an app to work with this data flow, it kind of takes the glamour away from two-way data binding.
Critique Summary
We do indeed need better frameworks to make it easier to develop larger HTML / JavaScript applications, but I don't feel that AngularJS has chosen the right approach. We do need to find a balance between the declarative HTML and the imperative JavaScript, but I feel that AngularJS has gone too far in the declarative direction. We do need to find ways to bind data to HTML elements / GUI components, but the one-way data binding in the AngularJS directives and two-way data binding in forms can be done better. But this is just my opinion. The whole purpose of this article was to help you form your own opinion.
An Alternative
As mentioned earlier I have been looking a long time for the "perfect" GUI toolkit, and when AngularJS turned out not to be what I was looking for either, I finally decided to make an attempt at a GUI toolkit myself.
The GUI toolkit is inspired by Flex and based on jQuery. The toolkit is centered around a page controller which scans the HTML page for elements referencing GUI components. For each GUI component reference found, a corresponding JavaScript object is created. These objects (components) are then kept internally in the page controller.
The page controller helps you avoid the "jQuery soup" which often occurs in larger jQuery apps. The page controller helps you break the code into components and modules, and keep references to instantiated components and modules etc.
The component approach avoids the DOM-centric code you can easily end up with in jQuery powered web apps, especially if they use jQuery plugins as GUI components. Therefore the toolkit does not use jQuery plugins for its GUI components.
The toolkit is called jqComponents (as in jQuery Components) and can be found at jqcomponents.com
Whether this approach will be closer to the "perfect" I am looking for than AngularJS is too early to say. Only time will tell. The initial proof-of-concept looks promising though (but I am biased of course).
More AngularJS Critics
I am not the only one to criticize AngularJS. I have read a few other great articles critizing different aspects of AngularJS than what I have included here. I have supplied a list of the best ones below. They are worth reading!
Why You Should Not Use AngularJS