So you’re curious what kind of truth about Angular.js I want to tell you. Then listen to me carefully, because I’ll say it only once:
I fuckin’ hate Angular.js!
Yep, if you’re looking for some kind of hater, it’s your lucky day – here I am! But before you crucify me, let me state my point of view clearly.
Tłumaczenie na polski już jest!
In many ways, I’m a web conservatist, who is very proud of it. It doesn’t mean that I avoid every wave of freshness, but I’m always a little bit sceptic about it – as I was about Angular.js. Sometimes I accept changes and add a new toy to my toolbelt, but sometimes I become one of the thoughest enemies of the new kid on the block. As you have probably already noticed, I didn’t fall in love with the new big thing from Google. And I have my own reasons, which I want to somehow rationalize and sum up (and maybe – like a good Dalek – make someone else also a hater of Angular.js!) – some of them are strictly subjective, some of them try to be objective (or rather intersubjective).
If I want to be objective, I can’t be writing this post in some kind of void, so I have prepared some resources to back up my reasoning. Of course that rant won’t be complete without the Bible of all Angular.js critics: the famous What’s wrong with Angular.js article and to tell you the truth, it was the biggest impulse for me to even look further into Angular.js (yes, I always want to laugh check if someone is right). There are also less known You have ruined javascript, Think Twice (or Thrice) Before Using Angular and – not pointed directly against Angular.js – Stop Breaking the Web. In the opposite corner of our ring, we have Why Does There Have to be Something Wrong with AngularJS?, What’s “Right” with AngularJS? and more informative, less emotional Add interactivity to HTML with AngularJS. Of course I’ll also use Angular docs and source code and refer (quote code) to 2 Angular.js’s apps: the new & simple one and the older & complex one. There will be also some references to other articles, not connected with Angular.js, but about developing web apps in general. I want to freely move along the path carved by the (in)famous What’s wrong… and compare it to the other points of view, commenting with my own ideas and reflections (gosh, it already sounds like a serious scientific research, not a simple rant!).
Having said that, let’s start the party already!
-
App logic and structure expressed in HTML, which is enchanting for beginners (Look’ma no JS, magic!), but terrible for real development. We are developers, we write and debug code.
It’s the first (and for me one of the most important) reason why Angular.js is wrong: declarative approach. We all know it well. The hype for „declare all the things” was begun around 1998, when W3C started working on XHTML. At the first glance, it looks promising and refreshing. Is it? I don’t think so.
If I call something a „JS framework”, I expect that it uses JS to accomplish tasks. When I talk about Angular.js, I see not JS, but – as they call it – enhanced HTML. Binding function to some elements? Use
[ng-click]
attribute. Trying to create button, which should be disabled under specified circumstances? Use[ng-disabled]
. Doing something else? Use[ng-whateverYouNeed]
.<button class="btn btn--radius" ng-click="resetForm()" ng-disabled="criteria.$pristine">Reset</button>
We fought for unobtrusive JS for years and when I finally became happy that the war is over, mixing JS with HTML came back through back door, using something worse than DOM0 HTML attributes. Yes,
[ng-click]
is worse than[onclick]
. While the latter is native (and, because of that, its’ behaviour is standardised, predictable, probably optimised by browsers and their DOM parsers), the former is just an unkown attribute. And as it, Angular.js is forced to parse the whole DOM tree just to get that attributes and, after doing that, it must parse the attributes themselves. It doesn’t sound funny. What’s more, Angular.js sanitizes that attributes and compile them into JS functions, that have access only to defined scope… So the framework must have some kind of HTML sanitizer & parser and it’s somehow big. And there is also parser to get over templates.<label>{{ key | camel2Space | capitalizeFirst }}</label>
Templates in HTML? Tempting, but impractical. Yup, I know that there is
template
tag in Web Components, but it works in very different way. The main difference is thattemplate
is used to create a DOM subtree, that it’s not included in main DOM tree. Thanks to that, nothing is visible to user until we decide to show something. Every change is made through DOM methods, because… everything is in DOM. Angular.js’s approach is completely different, as Angular.js treats the page as a template. It’s not elegant. And there are just silent errors, when something went not so well.Why I found page as template as not elegant? Because page is a view, not a template. And in the ideal world, template shouldn’t depend on DOM. DOM based templates are browser-centric – tight coupling between product and device on the device agnostic Web sounds ridiculous. The situation in Angular.js is even worse, because silent errors can lead to situation when view is based on not well compiled template and it becomes unusable.
So what’s the case with
template
from Web Components, that it’s enhanced by Polymer to understand{{mustaches}}
? Is it also so bad? Well, yes – it’s bad, but it also strictly separates view from uncompiled template, just because its DOM tree is completely decoupled from page’s DOM tree. And it’s the main diffence. All other points against Angular.js’s templates seem to be also applicable totemplate
tag (especially in its Polymer flavoured kind!). But in some cases I find DOM methods friendler than string-based templates –template.querySelector('.img').src
is just… sexy.Going back to our attributes – John Papa said:
The declarative nature works great at removing oft used features and making them simple
Well, I find binding JS methods to DOM elements in JS simpler than declaring them directly on that DOM elements. And it’s probably going to be less repetitive.
<button ng-click="doSomething()">Do something</button> <button ng-click="doSomething()">Do something</button> <button ng-click="doSomething()">Do something</button> <button ng-click="doSomething()">Do something</button> <button ng-click="doSomething()">Do something</button> <button ng-click="doSomething()">Do something</button> <button ng-click="doSomething()">Do something</button>
vs.
bindAction('.do-something', doSomething);
Of course we must implement our
bindAction
function, but it should be dead simple, usingdocument.querySelectorAll
+element.addEventListener
. And it clearly decouples our JavaScript from our HTML. Because JS should be in JS, not in our HTML. Modifying HTML just because our JS was changed is just funny. Very good example of decoupling HTML, JS and CSS is BEM methodology (I like to call it „architecture”, rather than „methodology”) – the only „glue” between these layers is[class]
. No more attributes, like[ng-click], [on-click]
(maybe they are just the ways to fool CSP to think that they’re not connected with scripting in any way?). Our HTML is dead simple (and fully semantic) and we have some nice bindings in JS and CSS (they even nicer when we got to know the concept of BEM tree).I also don’t get Angular.js’s love to repeat the same information – in JS, in models (
model.$dirty
,model.$pristine
) and in HTML (.ng-dirty, .ng-pristine
). Even the argument about styling is not good – in that case we need just one class (and in JS we need just one state), because it can be true (present) or false (absent).model.$dirty
means the same as!model.$pristine
– nothing more. I also don’t get developers that come up with solutions, like[focus]
directive, which does exactly the same thing as native[autofocus]
, being worse at it not recognizing that there was some other field focused and probably user don’t want to move focus while typing. Reinventing the wheel in its pure form.Rather than building upon the DOM, we build upon our application’s data.
It looks like an advertisment of jQuery. Yup, using Angular.js we don’t need to think about DOM… but we actually build everything upon the DOM! Angular.js’s main core is HTML/DOM parser – so it’s just silly to think that we don’t need to think about DOM, working with that framework. It was the cancer of jQuery, which created the cast of jQuery-only developers, not knowing anything about the inner workings of DOM or JS. How can anyone become a magician, not knowing the magic? Maybe he even becomes popular, but one day he would just fuck up some tricks.
AngularJS absolutely does a lot of HTML parsing. But, if you do any maintenance of apps or care much about productivity then the declarative approach AngularJS provides is refreshing.
Well, the idea of enhancing HTML is refreshing… before we realise that we’re in hell. First of all, we tightly couple our JS with HTML and it’s not good (hey, show me the MVC framework – it doesn’t have to be written in JS – that states that view and model or view and controller should be so tightly coupled!). In Angular.js the HTML is even more important than JS itself – the HTML is the one that dictates how elements behave. The view also decides which controller is going to be used. It sounds crazy and I can’t stand it. Such an architecture moves the management of our app to something that is a hybrid of view and template.
Not yet convinced? Angular enables us to create custom elements easily, as well as modularising our functionality. We can write a complex web application like so (this is a simplified snippet of one of our production Angular applications):
<header user="currentUser"></header> <product-list ng-model="products"></product-list> <checkout ng-model="shoppingCart"></checkout>
No, it’s not convincing for me. Angular.js (in 1.x branch) is not based on Web Components, so that structure means nothing. Yes, it means nothing. Semantics? There aren’t any. Some nice DOM addons? Nope. Accessibility? Neither. So what do we get? Verbosity (it’s the same issue, as with „Semantic” UI). Angular.js even doesn’t treat that tags as custom elements, but as unkown DOM elements, which can be used in some tricky JS.
It could be done better with Web Components (and will be in Angular 2.0), but it still doesn’t solve the problem. Web Components, as I see them, should be treated as the mean to create „semantic” (for developer) GUI. It’s a technology for creating interactive views, composed from independent, isolable modules. And they should not include any logic not connected with UI. Declarative router is just insane. Application/bussiness logic should be outside Web Components and interact with them by events. With that we can create reactive, responsive GUIs, that are still highly decoupled from our application. I have written about it (in Polish) some time ago. And situation is not happy. Web Components are not accessible and creating tags like
application
means nothing without enough ammount of ARIA. ARIA is the foundation for „developer’s semantics” and without it all custom tags are just silly.Going into „declarative apps” can create some totally insane ideas, like HTML6 (HTML with XML namespaces? RLY?) and putting logic into HTML attributes is one of them. Not mentioning that invalidating HTML just because
[data-attributes]
are so passé seems somehow funny. -
Two way databinding is an anti-pattern.
Strong statement and not so true. 2-way data binding can help build applications faster and in more effective way. In fact I like that technique, but… I don’t like the way that Angular.js uses it. I’m disgusted by the only right declarative way to do it and I prefer using DOM Mutation Observer/events & getters/setters from ES5 to accomplish it. Probably BackBone’s way is nearest my heart at the moment. It’s just a matter of preference, I think – I really don’t know what are the performance’s differences between that two approaches.
Dirty checking, accessors (Ember and Backbone),
Object.observe
and all that stuff. Wrong! It’s slow and brittle and it will consume mobile battery like hungry dog, for no reason. You don’t need it. Use Facebook Flux rather.Well, I find dirty checking… dirty, but I like the idea of accessors, based on previously mentioned technologies.
Object.observe
+ Mutation Observer also should be a nice tool.Model.bindTo
or something similar – it’s how I see 2-way data binding. Everything declared in JS and view stays clean and small. Maybe useWeakMaps
andProxy
to create nice API (but it’s still a future, not today…). Something like:var user = new User({ name: 'User' ,email: 'mail@mail.mail' ,age: 100 }); user.bindTo([dom, elements]);
Why there is no place for bindings in HTML?
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.
And I totally agree with it. HTML is language that provides semantics for content. Nothing more. It’s not a template language (if you want declarative template language, look at XSLT and some other crazy XML stuff) nor programming language, that can provide some logic for your app. It’s all about semantics.
Bindings in JS – yes, bindings in HTML – no.
-
Duplicated app structure with obsolete
angular.module
. Almost for every app feature you have to 1) change HTML 2) change its controller.I think that I ranted enough about HTML, so we’re left with
angular.module
. How this point was commented by defenders of Angular.js?Angular motivates you to use modular components. It makes it easy to break things apart and decouple. So I’m not sure why this is a bad thing to create modules.
Angular provides the ability to create modules that can be combined with other modules to promote re-use and simplified maintenance. That’s one of the big reasons I like it so this is another comment that made me laugh but also wonder exactly what the author meant. There are certainly improvements that can be made here (lazy-loading of modules, dynamic loading of specific scripts, etc.) but it works well and it’s going to get even better in version 2 as the entire framework becomes more modular – including things like dependency injection.
I’m nearly certain that they missed the point here. The problem is not with modules, but the linking between modules and HTML, via
[ng-controller]
. It’s completely needless – view shouldn’t even know about controller!But, as they also stated, modules in Angular.js are not perfect. The main problem, visible to me, is the fact that Angular.js came up with propertiary module syntax. It’s not compatible with AMD or CommonJS and it’s bad, because it’s reusable only in Angular.js’s context. The situation is clear here: we had 2 standards (3, if we count ES6 modules or even 4, counting UMD… ok, 5, with Melchior.js’s Chainable Module Definition) and someone creates the next one, so we have now 3 standards (the Angular.js’s one is a standard just because Angular.js is „a standard”). What’s even funnier it’s the fact that Angular.js’s modules have syntax that is very similar to AMD.
var services = angular.module('guthub.services', ['ngResource']); […] services.factory('MultiRecipeLoader', ['Recipe', '$q', function(Recipe, $q) {}]);
It’s better in Angular 2.0, as it uses ES6 modules syntax. Of course the problem with it is that noone today supports that syntax (and using ES6 just to use it is ridiculous).
Angular.js’s modules should use UMD syntax, not (re)invented one, to became a part of the true JS environment.
-
Angular is slow.
If I had a dollar for every time I’ve heard someone say that about a framework over the years I’d be retired! People love to make generalized statements like “Framework X is slow” if they don’t like it and want to turn others off to it.
Well, I don’t want anyone to earn money because of my words, but I really don’t like Angular.js, so I will say it:
Angular.js is slow
(and look – some new & shiny stats, that somehow back up my words!).Yes, it’s slow. And I’m not speaking about real performance of that JS code – it doesn’t matter at that case. Angular.js is slow, because perceived performance is bad. Proof? Just look on that demo – you’ll probably see FOUC (or rather FOUT – Flash Of Uncompiled Template). That enemy was defeated years ago and now – alongside with DOM0-alike event bindins – is back.
The problem is caused by treating the page as template. It must be downloaded, parsed and finally it’s usable for user. Of course, we can prevent FOUT by moving JS to
head
, but it’ll probably also harm perceived performance, blocking page until JS is downloaded. And no, Polymer’s approach (body[unresolved]
that hides entire page until all Web Components are parsed) is also bad. To tell you the truth, there’s no good way to solve it. Well… There is one, but it’s troublesome, so let’s move to it… -
No server side rendering without obscure hacks. Never. You can’t fix broken design. Bye bye isomorphic web apps.
Well, server side rendering would deal with the problems described in the previous point, just because Angular.js is always going to be slower than rendering the content on server. Twitter knows it well. But it’s not about performance only. No server side rendering is a big deal, unfortunately completely misunderstood by Angular.js’s defenders.
My best guess is that this has to be a reference to Search Engine Optimization (SEO). So let’s assume that is the case. This is probably the biggest area of improvement that is begging for a better solution in the SPA space. Some companies must have SEO ready applications and it is not simple as it should be to use it with Angular. However, there are ways to tackle it such as prerender.io and these techniques.
More than likely he’s referring to Search Engine Optimization (SEO) issues which is the Achilles’ heel of any client-side framework – which he conveniently fails to mention.
Ehmmm… No, it’s not about SEO. It’s about USER! I mentioned perceived performance before, but even this is not the main deal here. The biggest concerns are accessibility and usability. What’s about Progressive Enhancement? Noone, really? You must be kidding me. And no, it’s not for people without JS – it’s for everyone.
Strive for simplicity. Use progressive enhancement. Don’t do it for people who disable JavaScript. Don’t do it for people who use older browsers. Don’t even do it just to be thorough. Do it because you acknowledge the important of delivering content first. Do it because you acknowledge that your site doesn’t have to look the same in every device and browser ever. Do it because it improves user experience. Do it because people on mobile networks shouldn’t have to suffer the painful experience of a broken web.
Do it for the ones with slow mobile connections, for ones that don’t get your JS, because your CDN is down for a moment and for disabled ones, who use screen readers, like Jaws or NVDA. And Google will benefit from it too.
So what’s the problem?
Angular needs a DOM to work. This is unlike other approaches where string templates are used, or even a virtual representation of the DOM that is not coupled to an existing DOM. And in the server we don’t have a DOM available. At least not a real one. We have some libraries that create DOM structures that claim to be compatible, and they may be up to some extent, but they won’t be exactly the DOM that angular needs.
Building the whole application upon a DOM sounds totally insane and it probably is. Main part of Angular.js seems to be broken by design.
DOM dependence of Angular.js is the worst thing of it, because it’s killing the accessibility and usability with one shot. ARIA and showing state of changing elements to user? There isn’t anything like that. Content for someone with disabled JS? Nope. Content for ones with slow Internet? Nope (ok – there is content for them… when their sluggy browsers complete downloading JS and parsing template). Better semantics for user? No. Good perceived performance by prerendering site? No. There isn’t even proper URIs… Hashbangs are just retarded – especially today, when we have History API.
SPA is not an exuse here – developing an accessible app is our duty, because we develop for people, not ourselves!. And SPA with server prerendering still can be SPA (hey – Angular.js needs server to send template; so why don’t send compiled template?). And yes, web apps without JS don’t have sense… almost. Look at GMail – it’s (not so) good example here: it also works without JS, having second, simpler UI (not so good example, because by using PE they could create only one version and just bind JS through DOM methods in JS). Nearly every app in Angular.js is using some kind of RESTful service – so without JS that info is still there. Angular.js could consume JSON and browsers without JS – full views. Accessible? I dare you.
And, well, I know that Angular.js was developed with SPAs in mind, but today is used everywhere (just like jQuery), breaking the Web.
PE is the forgotten foundation (in Polish) and without it I can’t even look into Angular.js’s direction – it breaks all principles I believe in.
-
Angular is hard to learn. It’s irony because Angular itself is promoted as easy framework for beginners. But it’s very complicated kind of easiness. You have to learn a lot of Angular specific patterns useful only in Angular world. Yeah, it’s result of bad design. This is both sad and ridiculous. Abstract layer can solve many problems, except problem of having too many abstract layers.
I can’t agree more: Angular.js tries to exchange JS with Java. In the old days, when I wanted to instantiate object in JS, I wrote
new Object
. Now I must have a service that provides the provider of factory, which inherits from factories’ factory… Gosh, no. Just no. When I want to create new object, I create new object. Period.Yes, abstracting things is good, but overabstracting them is one of the worst things I can imagine. And Angular.js is the best example of it. When looking into Angular.js’s code, I even found provider of
window
object or provider of jQuery wrappeddocument
. Well – Angularize all the things?We had a good thing, you ruined it. We had an escape route from that ridiculous enterprise hand-holding bullshit and instead of learning how to fucking code you’ve just brought your factory provider providers with you into what was once an okay place to get stuff done.
And that’s it – Angular.js is overabstracting many things.
I had a problem and thought to use Angular so now I have a problem directive factory
-
Vendor lock, Google not using it… Maybe we’ll just move along.
-
Will be rewriten entirely soon, which is a good thing for framework, but pain for you.
It will be definetely a pain, just look into Angular 2.0 code: ES6 (or AtScript) & Dart, modules in modules, even more classes than now… It’s totally different framework, with different approach, different syntax, different differences… Only name is the same.
I don’t know if Angular 2.0 would be better, but I know one thing: it’s still overabstracted.
-
It’s easy to see how Angular itself could change the face of web design. Dedicate a part of your team to building a part or the whole of your application in Angular – I promise, you’ll never look back.
Maybe too optimistic…
-
So, Mister Hater, what would you recommend?
Ironic reader of mine
I’m glad you’ve asked. There are some new heroes, like Taunus or super simple Mithril (it can’t be simpler than that, I dare you!). The old ones are still powerful, like BackBone.js. And… that’s my types. Not many of them exist, but I think that this list is long enough.
I think that Angular.js doesn’t enhance HTML – it breaks it. It also breaks JS, overabstracting it. It can be more performant and for me it’s behind today’s trends for simplicity.
Yes, there is need to evolve and enhance our Web, but I think that Angular.js’s road is a dead end one.
Angular is not even used by any Google products, as far as I know. That’s meaningful.
I haven’t investigated it deeply, but people say that it’s used by YouTube internaly and DoubleClick.
But yup, it’s quite meaningful. Although I don’t think that something like GMail should be rewriten from stable technology to something new & shiny, even if this new toy is better. The same case as NASA’s systems, created in FORTRAN 😉
Most of your arguments are just silly. You haven’t use Angular by yourself, haven’t you? I’ve seen projects with more than 800 directives that were managable, no performance issues AT ALL (oh, btw, your example is just badly written code).
„DOM dependence of Angular.js is the worst thing of it, because it’s killing the accessibility and usability with one shot” – bullshit, we deliver applications for hospitals worldwide and conducted hundreds of tests on real patients out there. Honestly, when reading something like „There isn’t even proper URIs… Hashbangs are just retarded – especially today, when we have History API.” I really think you don’t know anything about Angular… Very poor post, indeed.
I played with it for a while – not anything big, but enough to get known of it.
Ok, Angular can be fast, but according to some newer research almost everyone is writing bad code…
I love when someone cut out the whole context… Is Angular totally accessible out of box? I don’t think so. Also it was said in the context of server-side rendering, which is obligatory in many apps.
Angular’s router uses hashbangs by default and it’s just ridiculous when History API is a real standard. Yes, it can be changed, but why default config is just… silly?
I’d love to see some performant, server-side-friendly app written in Angular. For now I haven’t seen one.
Great article Mr Hater 🙂