Hi, Habr!
Many people liked our past translation of the
non-traditional review React , and, of course, people began to compare React with the popular AngularJS. Today we publish a translation of the article “An Unconventional Review of AngularJS” by the same author (James Shor, the lead project of Let's Code: Test-Driven JavaScript). Angular fans, please keep calm.

')
AngularJS is all I expect from the framework. And this is not good.
In November, December and January, I reviewed AngularJS for the “front-end frameworks” series as part of the Let's Code JavaScript project. In total, I spent 40 hours studying, programming and solving problems. As usual, my goal was to learn AngularJS by creating an
application .
Angular is probably the most popular front-end framework right now. It is developed by a team from Google, which immediately inspires confidence. It is so popular that it is included in the acronym. Angular is part of the so-called
MEAN stack : MongoDB, Express, AngularJS, Node.JS. The most advanced technology.
Angular describes itself as a tool for improving CTML. It allows you to extend CTML with new definitions — directives — that turn a static CTML document into a dynamic template. Directives can be attributes or tags (or even comments or classes, but this is not a completely ordinary story), and they turn a static CTML document into something living and breathing, at first glance, without adding JavaScript.
The best example is the famous two-way binding. The HTML template can contain variables, like most template languages, but in the case of Angular, your page is automatically updated with each variable update.
For example, in the
application that I wrote for the review, there is a table that changes when the configuration fields change. Here is a code snippet that renders a row of a table. Notice, there is no work with events or state monitoring ... Just a template that describes cells in a row. Angular does everything automatically.
Magic.
With such examples, it is easy to understand why Angular has become so popular. With him, hard problems seem trivial. But will he stand the test of time?
Unconventional review
Too many frameworks are trapped: it’s easy to start with them, which is very cool, but later it’s very difficult to maintain and extend your code. This is not very cool.
So when I review the framework, I don’t look at the usual criteria for performance, popularity or size. No, I want to know the answer to one important question:
Over the next 5-10 years, when will I support my product, will this code bring more benefit or suffering?Most frameworks are designed in such a way that they save time on the initial creation of a product. But these costs are trivial compared to the cost of supporting your application for several years. Before recommending a framework, I need to know that it will stand the test of time. Will it grow and change with me? Or will I find myself in the shackles of a barely fit Legacy after three years?
I look at five common dangers.
1. Closure (in-lock). When I decide to switch to a new or better framework (or library), how difficult will it be to switch?
2. Hard architecture (Opinionated Architecture). Can I solve problems in the way my application needs, or should I obey certain ideas developed by the authors of the framework?
3. Adverse complexity (Accidental Complexity). Do I spend time solving my problem or struggling with the framework?
4. Testing. Can I just test my code using standard tools and without much hassle with mock objects?
5. Server-side Rendering. Will people wait for JavaScript to see something useful? Will I have to
dance with a tambourine to make the search engines index my site?
I rated Angular in each category with a (aiii!), (Buee!), Or ⚇ (neither there nor here).
1. Closure - (bueee!)
No questions asked: Angular locks you in its frames. You specify an interface with specific Angular-directives, in specific Angular-templates, using specific Angular-definitions and code. There is no way to abstract from it. If you decide to switch - you will rewrite everything from scratch.
This is a common practice. So ordinary that it usually gives a reason to put a meh-face "neither there nor here." But Angular tried to earn it "buee".
First, Angular wants to be the master of all your client code. If you are writing an application with Angular, this means that you must use Angular-specific validations, write business logic in the form of Angular-specific services, and connect to the back-end through Angular-specific services.
Secondly, the Angular team gives us to understand that the cost of support for them is not a priority. Support for IE 8 was lost in Angular 1.3. Angular 2 was heavily rewritten, forgetting a few key concepts of the current version. This will likely cause the entire application to be rewritten.
I repeat: your entire front-end is locked, and even if you don’t change anything, you will most likely have to rewrite something. Rewriting is a terrible idea. You will spend a lot of money and time to repeat what you already have. A framework whose roadmap has the idea to “rewrite the application” is unacceptable.
2. Hard architecture - ⚇ (neither there nor here)
Angular wants you to build your application in a specific format, but does not do so very explicitly. It is rather a "passive-aggressive architecture."
Tough architecture is a good idea in the short term, bad in the long term. In the short term, a rigid framework allows you to start quickly, without thinking about the structure of the application. But in the long run, too-rigid architecture limits the possibilities. As your requirements grow, the standards of the framework begin to get in the way of you more and more.
Angular's passive-aggressive architecture includes the worst of both worlds. It makes assumptions about the design of your application, but it does not help you understand these assumptions. I'm not sure that I understand it even now, but this is what I managed to get:
“At a fundamental level, Angular believes that you are using stateless" service "objects for logic and simple objects for data structures (objects without methods) to maintain state. Services are essentially global variables; Most functions can use any service, referring to them by a special name. Data structures are stored in $ scope, they are associated with templates and directives. Data structure objects are managed by “controllers” (“glue” associated with patterns and directives) and services. ”I'm not a fan of this architecture. Separating the state from the logic, Angular breaks the encapsulation and shares closely related concepts. Instead of putting the logic next to the data with which it works, Angular wants you to spread the logic throughout the application. There is a risk of “
operating a shotgun victim ”: any change results in a bunch of minor fixes.
The
tutorial application demonstrates this problem. The application displays a list of smartphones, and “phone” objects are a key concept. Ideally, a change in the internal structure of the phone object should not affect anything. But they are just
objects with data , so changing them will entail changes in other parts of the application:
the phone
list output pattern, phone
information output pattern , and
both controllers for these patterns.
I prefer rich domain objects that contain state and business logic. So I can make changes without breaking anything. For my example application, I used a
rich domain layer that relies on an
immutable object with a value. Angular's passive-aggressive architecture does not support this approach — sometimes I had to
deform my code to satisfy Angular's assumptions, but this was not an absolutely impossible task. Could be worse.
3. Side complexity - (buee!)
Angular is famous for its steep learning curve and poor documentation. I think these are symptoms of a larger problem. This is not a documentation problem, this is an Angular problem. He just has a bad design. Here are just a few flaws that I noticed:
Leaky abstractions. To use Angular in a non-trivial project, you will have to go deep and understand how it works under the hood. You will have to understand scopes and how they work within the framework of prototype inheritance: digest loop; $ watch, $ watchCollection, and $ apply; and a bunch of other things.
Magic Strings is an attempt to circumvent the poor unity of design. Often you will have pieces of code that are related to each other, but are in different files.
Incomprehensible characters everywhere. In Angular there are several tiny languages ​​that will have to be inserted into strings in your application. Get ready to learn the difference between "=", "&", "= *", and "@"; “E”, “A”, and “EA”; operator "|"; and other hieroglyphs.
Elusive incompatible differences. Problems can be solved in several ways, each path has minor but important incompatibilities. For example, how you create a controller will affect the syntax in the template and the way the variables are stored in $ scope.
Tendency to quiet file. It is easy to make a mistake, do something wrong and get no indication of the cause. Wrote “E” where you should write “A”? The application just stopped working.
When I
wrote the same application for the first time on the React, it took me 28¾ hours. I wrote the same application on Angular for 39½ hours, despite the fact that I could use part of the code from the first attempt on React. Ten o'clock on top. The reason is the excessive complexity of Angular.
4. Testing - ⚇ (neither there nor here)
Angular considers testing very important. One of its main features - dependency injection (dependency injection) is made specifically to simplify testing.
Given this trick, I was surprised at how bad things are with testing in Angular. It focuses on the logic of testing in controllers and services, but gives almost nothing to test the behavior of the user interface. There is no support for simulating browser events and there is no way to unit-test HTML templates. Custom directives can be tested, but testing a directive, within which there is another directive, is
scary .
Angular focuses on the power of unit testing for business logic. But the only reason is that the architecture itself provokes to place business logic in the UI (in particular, in controllers and services). Good architecture would put business logic in UI-independent objects.
Many aspects of Angular are felt as plasters over self-cut cuts.
By removing the business logic, as was the case with my application, it remains only to test how Angular renders CTML and reacts to events, and Angular did not support my desire to do so. The Angular team advises using a specially made end-to-end framework for testing
Protractor .
End-to-end tests are slow and fragile. Their number should be
minimized , and not rely on them as the central idea of ​​a testing strategy. Fortunately, by placing the UI of my application in custom directives, I was able to unit-test Angular. So he earned a neutral face here “neither there nor here,” but if you look closely, you can see one small tear on it.
5. Server Render - (Bueee!)
AngularJS is not designed to work on the server. No surprises, but it is worth considering.
Bottom line: avoid it.Working with Angular is a torment. Each step is a new feature or test, and by the end of my review it really got me. Perhaps if I did everything as Angular wants, rather than trying to implement my design, it would be easier for me, but my goal was to explore the possibilities of supporting the project in the long run, rather than do everything as quickly as possible.
And in this respect, everything is bad. Angular is a complex framework, and it has grown into something weird. It is popular, but not good, and I suspect that it will quickly be forgotten when better alternatives appear. Angular 2 is already on the horizon, so by taking Angular today, you are sentencing yourself (most likely) to rewriting the application after a couple of years. And although future versions may correct these shortcomings, today's Angular is a poor choice. Avoid it.