Much time has passed since AngularJS birth (given the fact how fast front-technologies evolve). Now on the internet, there are a huge amount of posts praising this framework, and the critics are not so many as it deserves. But such posts are slowly beginning to appear, and it pleases me. I hope industry will give up AngularJS, as it gave up MooTools, Prototype, %some new JVM language%, %another-super-modern-technology%. I donât know why, but in the IT field such revolutionary technologies that raise the noise and then disappear appear quite often. A good developer should be able to distinguish another fancy technology from the running tool. In order to do this it is very important to look at things critically. My post is a compilation of the most important conclusions from other posts and my personal thoughts.
Angular creates a good âwow-effectâ, when you see it for the first time: âwow, I wrote ng-repeat, and implemented this logic only with tags, and it updates by itself!â, but as soon as you have to implement a real application, but not another TODO-list, then it all becomes very frustrating. I just want to say that I know the framework well, even better than I would want to know it. Iâve been using it for two years already. So what is wrong with AngularJS? There is no definite answer, as there are too many flaws that create the appearance of the framework. One would say it is ill-conceived architecture.
Two-way data-binding
There is a fundamental rule in programming, it says that explicit is always better that implicit. All these $watchâs is an implicit invocation of event handlers. In reality, events occur when a user clicks the item, not when the model changes, so when you write on AngularJS you need to think the following way: âthe user clicked the button and the model has changed, now we need to listen to changes on this model and call the handlerâ and not âthe user clicked the button and call the handlerâ. Itâs not how how events are handled in principle. This is the same as when you fetch data from the database and write these data into the model, and only by the change of the model callbacks are called. Itâs nonsense! Even in Java, a language which has no anonymous functions (when I was writing in it) callbacks were implemented with anonymous classes. Because it is explicit and because this concept describes the real situation. Even the next version of EmberJs will not use two way data binding.
Also two way data-binding means that changing anything in your application will trigger hundreds of functions that monitor changes. Itâs slow, and it is especially all turns bad on mobile platforms and when you write something something complex. And it is a fundamental part of the framework. Angular even imposes restrictions on how rich UI you can write. And what is most interesting is that this is not some ephemeral or distant limit you will never experience. Itâs only 2000 watchers, and if you develop more or less large application, you will undoubtedly run into this limitation. And then you will have to fight with the framework. Angular introduces the possibility of one way data-binding, to avoid performance problems. Create a problem and solve it with a hack (ill-conceived architecture#1). Take a look at how many people who are struggling with the performance of angular. Even if you google âangular performanceâ you will be astonished with the number of articles describing how to accelerate AngularJS. Isnât it a sufficient argument in favor that angular is slow?
There is another point, sometimes you need two way data-binding, but UI is already slow. For example, when the user loads the page he may see {{ expressions in brackets }}. Hmm⌠itâs not a bug itâs a feature! We need to introduce a directive, which will hide the UI so that user wonât see flickering, and wonât distract the framework from solving its fictional problems. And for these purposes AngularJS introduces new directives: ngCloack, ngBind. These problems wouldnât happen in the first place if a more nimble framework was used which immediately shows data as soon as they appeared. In return it takes time for angular to display these data (in additional to rendering time). AngularJS again creates problems and solves them with hacks (ill-conceived architecture#2). And if the framework has such problems with scalability, doesnât it mean that something is wrong at the fundamental level? Concise and well written technologies scale well as a rule.
Dependency Injection
The next example of how angular makes you suffer is Dependency Injection. In Angular dependencies are injected by the name of an argument.
function MyController($scope, $window) { // ⌠}
Here Angular calls .toString(), gets the names of the arguments and then looking for them in the list with all existing dependencies. Searching for a variable name is ok, given the typing of JavaScript, but the problem is that it stops to work after minification of code. When you minify your code, it stops working, since variables are injected by name. You either have to use this syntax:
someModule.controller(âMyControllerâ, [â$scopeâ, function($scope) { }]);
or this
MyController.$inject = [â$scopeâ, â$windowâ];
but itâs unclear why it was necessary to introduce several ways to do the same thing, but even more unclear why it was necessary to introduce a deliberately broken way (ill-conceived architecture#3). The next important thing is how dependencies are declared. Before you inject a dependency it needs to be declared. How would you do this:
injector.register(name, factoryFn);
Where name is the name of the dependency and factoryFn is the function that will initialize the dependency. Thatâs it. Literally the single sentence described a very concise idea. Now look at what angular offers. AngularJS introduces 5 new entities: provider, service, factory, value, constant (ill-conceived architecture#4). And each of them is different from each other, but essentially they are all the same. Most importantly, they can all be easily replaced with a single method that I described above. But it is too easy and not interesting, letâs make people suffer better!
Debugging
Debugging is complicated in itself, but itâs not enough, it needs to be more complicated! Angular feeds your pain and suffering. Errors in bindings donât fire at all, for example in this code
<div ng-repeat=âphone in user.phonesâ> {{ phone }} </div>
if the user is undefined, no error will be fired (ill-conceiving architecture#5). Moreover, you canât put a breakpoint inside {{ this expression }}, because it is not JavaScript. Letâs go further, errors that occurred in JavaScript are caught by the internal angular interceptor, and interpreted by browser as caught errors (everything that happens in AngularJS, stays in AngularJS). Because of that you have to switch on the flag Pause On Caught Exceptions (so that debugger stops on all exceptions, but not only on uncaught (usually uncaught exceptions are worth attention)), thatâs why you need to go through all internal angular exceptions while it is initializing (ill-conceived architecture#6) and only then you get to your real exception. And then you will see the following stacktrace:
The error fired from the digest cycle and it means, that it could be caused by the change of any variable in the whole application. You will never find out where errors come from using debugger (ill-conceived architecture#7).
Scope inheritance
It is without a doubt the most common error that absolutely every AngularJS developer faces. For example, if one writes this code:
<div ng-init="phone = 02"> {{ phone }} </div> <div ng-if="true"> <button ng-click=âphone = 911">change phone</button> </div>
then by clicking on the button the âphoneâ variable wonât change, while in this code:
<div ng-init="phone = {value: 02}"> {{ phone.value }} </div> <div ng-if="true"> <button ng-click=âphone.value = 911">change phone</button> </div>
everything works as expected. Or for example in this code AngularJS behavior is extremely not intuitive: jsfiddle.net/1op3L9yo/. There is even a new syntax for declaring variables in a controller using this notation, which fixes this problem. (ill-conceived architecture#8). Obviously, this behavior is absolutely useless and very dangerous:
- If you base your logic on scope inheritance, then it becomes very hard to test it (you have to initialize the state of the parent controller).
- Logic becomes more complicated and implicit (you use a variable that isnât declared in the current module).
- Inherited variables are similar to global variables (from child scope you can access absolutely any variable of any parent scope). And everybody knows that global variables are evil.
- It increases the amount of theory you need to know: prototypal inheritance, when transclude creates a new scope and when it doesnât create a new scope, you need to know how scope inheritance work with ng-repeat, ng-if, ng-switch, you need to know why a directive has 3 different types of scopes and so on.
Just by the amount of questions on stackoverflow.com and articles on the internet it is clear how confusing this topic is. That is it, by the way, what are you doing tonight? Even in advanced style guides people advise to avoid using $scope. During 2 years of work with AngularJS, I havenât found any places where referring a variable from a parent scope would be a good idea (except for standard directive like ng-repeat, about the implementation of which I, as a user of the framework, do not care). Most interestingly, this behavior could be easily avoided by removing the possibility of scope inheritance or by creating some wrapper over JavaScript objects (for example ExtJs creates a wrapper over objects for imitating OOP). As a rule, frameworks should hide shortcomings of the platform and provide in its API more logic and transparent behavior. In return angular makes you fight with this aspect.
Directives
We come to the most interesting part, directives is the Holy of holies of angular. Everybody is crazy about them, people just worship them for some reason! But Iâm pragmatic, here is complete syntax of the directive declaration. To understand this syntax and why you need it, you really have to spend a lot of time. Instead of writing WHAT to do, you think about HOW to do it. The sad thing is this complexity is useless. There is no logical reason to separate logic for 3 methods (compile, link, controller), all this can be easily implemented in a single method. Again, look how many problems it causes. Also take into account that some functions get scope, some not, some execute only once, some every time with $digest cycle, and every directive also have the priority. Even in order to integrate some code in the angular world, for example some jQuery plugin, you need to wrap it in a directive. Because it would be too easy if developers could use ready solutions right away! Also itâs unclear why the controller function in the directive has the same name as a usual controller, they are used in completely different places and for different purposes, they have to have different names! (ill-conceived architecture#9)
Problems with people
Firstly, since the framework is overcomplicated, not many developers really know it well and it is hard to find such developers is very hard. Secondly, server developers wonât understand what is going on front-end and wonât be able to read the code at all. And this is a very huge disadvantage, as it creates a black box, which can handle only one person in the team, and if that person leaves, then no one can replace him. It would be ok if development of front-end really required such complicated tools (for example writing GUI in ExtJs is much harder, than in AngularJS, but in ExtJs this complexity is reasonable), but no, AngularJS is hard because difficulties are created on purpose. If you use ReactJs or jQuery then any server developer will be able to read the code.
Inability of server side rendering
If you try to use server side rendering for example to speed up page rendering, or for SEO (or both), then you will be disappointed. Since server side rendering adds logic into your HTML and AngularJS writes logic in HTML, there is no clear separation of concerns and as a result you get spaghetti code. Angular simply doesnât suppose that developers will want to speed up their pages, or want to be indexed by search engines, it isnât created for these purposes (ill-conceived architecture#11). Yes, you can go around using prerender.io (the service, that parses your SPA and gives you HTML files that should give to crawlers). But againâââitâs a hack, and not a solution of the problem.
AngularJS 2.0
Soon the next major version of the framework will come out, which breaks backward compatibility. Apparently, even developers of the framework realized that there is something wrong and decided to rewrite the framework almost from scratch. They donât add new functionality, breaking backward compatibility a little, they rewrite almost everything. It means that if you start a new project with the current version of AngularJS, then in the future you definitely will be overboard. Just think to yourself, would they rewrite the framework if it already was good?
Other points
I covered only the most noticeable problems, when the developer writes bad code, he writes it everywhere, so if you are going to use this framework, get ready to face not only these problems, but also many other problems, less noticeable, but still problems.
- Terrible documentation, you have to google a lot of information.
- Still no directives for Drag and Drop events (by the moment of writing). Itâs not a big problem by itself, but it indicates the quality of the product and the attention to details.
- When you write in AngularJS you put your logic into your HTML (ng-repeat, ng-show, ng-class, ng-model, ng-init, ng-click, ng-switch, ng-if). Existence of such logic is not as bad as the fact that it is impossible to test this logic with unit tests, this logic canât be debugged and errors donât fire from markup (but this code contains very important logic).
Conclusion
Most of the problems that I have described can be solved if desired (and I had to, I couldnât give up the project, that I wrote myself). But it is a dead end, it is like driving some very cheap car, instead of something expensive and reliable.âââIs it cold inside?âââDress warmly!âââDetails come off easily?âââYou should only drive on the straight road!âââIs it noisy at high speed?âââMake the music louder and you wonât hear the noise! And if something has broken, come to our workshop and we will fix it. Angular is the same cheap car. You can drive it, but you will definitely get huge problems. The fact that problems can be solved doesnât mean, that there are no problems. The only good thing that AngularJS has is that it forces developers to break their logic into modules, and code becomes more granulated, I donât see any other advantages of AngularJS. Instead of AngularJS it is better to use React by facebook (I havenât found in it any of listed problems) or Polymer (it is even not a framework, just a polyfill for web components), or something else, but on your own risk because there is plenty of negative feedback about Backbone, Knockout and others. I donât know why AngularJS is so popular, but it is definitely not because it is a good framework. That is why I strongly advise you not to use the current Angular version. I hope my experience will help someone to make the right decision about technology stack for the next project.