Menu

AngularJS: The Bad Parts

Below is a graph over the amount of searches for AngularJS versus a bunch of other Single Page Application frameworks. Despite the flawed methodology, the story seems to be pretty clear: Popularity wise, Angular is beating the shit out of the other frameworks. I spent most of last year working on a large project built on AngularJS, and I’ve gotten to know the framework in some depth. Through this work I have learned that Angular is built around some really bad ideas that make it a pain to work with, and that we need to come up with something better. Whatever the reason is for Angulars popularity, it isn’t that it’s a great framework.

The amount of searches for various SPA frameworks. (A less charitable interpretation of this data would be that Angular users have to search for answers more often than the others do.)

Bad Idea #1: Dynamic scoping

The scope of a variable is the part of the program where the variable can be legally referenced. If your system has variables, it has some concept of scoping.

Angular has a DSL that is entangled with the HTML and used primarily to express the data-bindings between the UI and application code. This has variables, and thus a concept of scopes. Let’s take a look at it. Consider for example ng-model:

<input type="text" ng-model="obj.prop" />

This creates a two way binding on the property prop of object obj. If you type into the input field, the property prop updates. If you assign to the property prop, the input field updates. Neat.

Question: What does obj.prop refer to in the second input tag? The answer is that it is literally impossible to tell what meaning of ng-model=”obj.prop” is by reading the code. Whether or not the two “obj.prop” names refer to the same thing depends on the runtime state of the program. Try it out here: http://jsfiddle.net/1op3L9yo/ If you type into the first input field first, the two inputs will share the same model. If you type into the second one first, they will have distinct models.

WTF?

What’s going on here? Understanding that requires some knowledge of AngularJS terminology – skip this paragraph if you don’t care. The part that says ng-if is what’s called a directive. It introduces a new scope that is accessible as an object within the program. Let’s call it innerScope. Let’s call the scope of the first input outerScope. Typing “t” into the first input will automatically assign an object to outerScope.obj, and assign the string you typed to the property like so: outerScope.obj.prop = "t". Typing into the second input will do the same to the innerScope. The complication is that innerScope prototypically inherits from outerScope, so whether or not innerScope inherits the property obj depends on whether or not it is initialized in outerScope, and thus ultimately depends on the order in which the user interacts with the page.

This is insane. It should be an uncontroversial statement that one should be able to understand what a program does by reading its source code. This is not possible with the Angular DSL, because as shown above a variable binding may depend on the order in which a user interacts with a web page. What’s even more insane is that it is not even consistent: Whether or not a new scope is introduced by a directive is up to its implementer. And if a new scope is introduced, it is up to its implementer to decide if it inherits from its parent scope or not. In total there are three ways a directive may change the meaning of the code and markup that uses it, and there’s no way to tell which is in play without reading the directive’s source code. This makes the code-markup mix so spectacularly unreadable that one would think it is deliberately designed for obfuscation.

JavaScript allows for optional dynamic scoping with the with statement. This is dangerous enough to make Douglas Crockford write books telling you not to use it, and it is very rarely seen in practice. Nevertheless, with-statements are similar to how scoping works in Angular.

Pit of Despair

At this point, I imagine some readers are eager to tell me how to avoid the above problem. Indeed, when you know about the problem, you can avoid it. The problem is that a new Angular user likely does not know about the problem, and the default, easiest thing to do leads to problems.

The idea of the Pit of Success is said to have been a guiding principle in designing platforms at Microsoft.

The Pit of Success: in stark contrast to a summit, a peak, or a journey across a desert to find victory through many trials and surprises, we want our customers to simply fall into winning practices by using our platform and frameworks. To the extent that we make it easy to get into trouble we fail.

Angular tends to not make you fall into the Pit of Success, but rather into the Pit of Despair – the obvious thing to do leads to trouble.

Bad Idea #2: Parameter name based dependency injection

Angular has a built in dependency injector that will pass appropriate objects to your function based on the names of its parameters:

function MyController($scope, $window) {
// ...
}

Here, the names of the parameters $scope and $window will be matched against a list of known names, and corresponding objects get instantiated and passed to the function. Angular gets the parameter names by calling toString() on the function, and then parsing the function definition.

The problem with this, of course, is that it stops working the moment you minify your code. Since you care about user experience you will be minifying your code, thus using this DI mechanism will break your app. In fact, a common development methodology is to use unminified code in development to ease debugging, and then to minify the code when pushing to production or staging. In that case, this problem won’t rear its ugly head until you’re at the point where it hurts the most.

Even when you’ve banned the use of this DI mechanism in your project, it can continue to screw you over, because there are third party apps that rely on it. That isn’t an imaginary risk, I’ve experienced it firsthand.

Since this dependency injection mechanism doesn’t actually work in the general case, Angular also provides a mechanism that does. To be sure, it provides two. You can either pass along an array like so:

It’s unclear to me why it is a good idea to have two ways of doing this, and which will win in case you do both. See the section on unnecessary complexity.

To summarize, there are three ways to specify dependencies, one of which doesn’t work in the general case. At the time of writing, the Angular guide to dependency injection starts by introducing the one alternative that doesn’t work. It is also used in the examples on the Angular front page. You will not fall into the Pit of Success when you are actively guided into the Pit of Despair.

At this point, I am obligated to mention ng-min and ng-annotate. These are source code post-processors that intend to rewrite your code so that it uses the DI mechanisms that are compatible with minification. In case you don’t think it is insane to add a framework specific post-processor to your build process, consider this: Statically determining which function definitions will be given to the dependency injector is just as hard as solving the Halting Problem. These tools don’t work in the general case, and Alan Turing proved it in 1936.

Bad Idea #3: The digest loop

Angular supports two-way databinding, and this is how it does it: It scans through everything that has such a binding, and sees if it has changed by comparing its value to a stored copy of its value. If a change is found, it triggers the code listening for such a change. It then scans through everything looking for changes again. This keeps going until no more changes are detected.

The problem with this is that it is tremendously expensive. Changing anything in the application becomes an operation that triggers hundreds or thousands of functions looking for changes. This is a fundamental part of what Angular is, and it puts a hard limit on the size of the UI you can build in Angular while remaining performant.

A rule of thumb established by the Angular community is that one should keep the number of such data bindings under 2000. The number of bindings is actually not the whole story: Since each scan through the object graph might trigger new scans, the total cost of any change actually depends on the dependency graph of the application.

It’s not hard to end up with more than 2000 bindings. We had a page listing 30 things, with a “Load More” button below. Clicking the button would load 30 more items into the list. Because the UI for each item was somewhat involved, and because there was more to this page than just this list, this page had more than 2000 bindings before the “Load More” button was even clicked. Clicking it would add about a 1000 more bindings. The page was noticeably choppy on a beefy desktop machine. On mobiles the performance was dreadful.

Keep in mind that all this work is done in order to provide two-way bindings. It comes in addition to any real work your application may be doing, and in addition to any work the browser might be doing to reflow and redraw the page.

To avoid this problem, you have to avoid this data binding. There are ways to make bindings happen only once, and with Angular version 1.3 these are included by default. It nevertheless requires ditching what is perhaps the most fundamental abstraction in Angular.

If you want to count the number of bindings in your app, you can do so by pasting the following into your console (requires underscore.js). The number may surprise you.

Bad Idea #4: Redefining well-established terminology

A common critique is that Angular is hard to learn. This is partly because of unnecessary complexity in the framework, and partly because it is described in a language where words do not have their usual meanings.

“Constructor functions”

In JavaScript, a constructor function is any function called with new, thus instantiating a new object. This is standard OO-terminology, and it is explicitly in the JavaScript specification. But in the Angular documentation “constructor function” means something else. This is what the page on Controllers used to say:

Angular applies (in the sense of JavaScript’s `Function#apply`) the controller constructor function to a new Angular scope object, which sets up an initial scope state. This means that Angular never creates instances of the controller type (by invoking the `new` operator on the controller constructor). Constructors are always applied to an existing scope object.

In the JavaScript specification, and in programming languages in general, the execution context is well defined as the symbols reachable from a given point in the code. It’s what variables are in scope. Angular does not have its own execution context any more than every JavaScript function does. Also, Angular does not “modify the normal JavaScript flow”. The program flow in Angular definitely follows the same rules as any other JavaScript.

“Syntactic sugar”

This is a quote from the documentation on providers:

[…] the Provider recipe is the core recipe type and all the other recipe types are just syntactic sugar on top of it. […] The Provider recipe is syntactically defined as a custom type that implements a $get method.

If AngularJS was able to apply syntactic sugar, or any kind of syntax modification to JavaScript, it would imply that they had their own parser for their own programming language. They don’t*, the word they are looking for here is interface. What they’re trying to say is this: “Provide an object with a $get method.”

(*The Angular team does actually seriously intend to create their own compile-to-JS programming language to be used with Angular 2.0.)

Bad idea #5: Unnecessary complexity

Let’s look at Angular’s dependency injector again. A dependency injector allows you to ask for objects by name, and receive instances in return. It also needs some way to define new dependencies, i.e. assign objects to names. Here’s my proposal for an API that allows for this:

injector.register(name, factoryFn);

Where name is a string, and factoryFn is a function that returns the value to assign to the name. This allows for lazy initialization, and is fully flexible w.r.t. how the object is created.

The API above can be explained in two sentences. Angular’s equivalent API needs more than 2000 words to be explained. It introduces several new concepts, among which are: Providers, services, factories, values and constants. Each of these 5 concepts correspond to slightly different ways of assigning a name to a value. Each have their own distinct APIs. And they are all completely unnecessary, as they can be replaced with a single method as shown above.

If that’s not enough, all of these concepts are described by the umbrella term “services”. That’s right, as if “service” wasn’t meaningless enough on its own, there’s a type of service called a service. The Angular team seem to find this hilarious, rather than atrocious:

Note: Yes, we have called one of our service recipes ‘Service’. We regret this and know that we’ll be somehow punished for our misdeed. It’s like we named one of our offspring ‘Child’. Boy, that would mess with the teachers.

Einstein supposedly strived to make things as simple as possible, but no simpler. Angular seems to strive to make things as complicated as possible. In fact it is hard to see how to complicate the idea of assigning a name to a value any more than what Angular has done.

In conclusion

There are deep problems with Angular as it exists today. Yet it is very popular. This is an indication of a problem with how we developers choose frameworks. On the one hand, it’s really hard to evaluate such a project without spending a long time using it. On the other hand, many people like to recommend projects they haven’t used in any depth, because the idea of knowing what the next big thing is feels good. The result is that people choose frameworks largely based on advice from people who don’t know what they’re talking about.

I’m currently hoping Meteor, React or Rivets may help me solve problems. But I don’t know them in any depth, and until I do I’ll keep my mouth shut about whether or not they’re any good.

Great article. I knew there’s something wrong with Angular terminology, but I got too skeptical after seeing DI magic to keep digging deeper. Thanks for sharing.

BTW. I don’t recommend Rivets. I interacted with a team working on a large UI project with backbone+marionette+rivets and debugging what rivets did is a horror story on its own. They use an older version and can’t migrate to newer (like with angular 1->2 …), so it might have gotten better, but the way it works for them is just sprinkling hard to understand magic here and there. And if you put an item view inside a bigger view and both of them have a certain rivets setup, the parent rivets will try to work with the item view too, and you have naming conflicts.

Thank you for this post. I’ve been struggling between Ember and Angular and trying to push it forward in a fortune 100 company. Of course they want to use what’s most popular, regardless if it’s the best choice. Betamax vs VHS?

I’m currently building a large scale application using Angular 1.x, and using things like yeoman and keeping our wits about us, we have been able to have pretty good success. The dynamic scope is the real issue and we can only hope 2.0 will fix obvious issues such as this. I realize this post is a year old, and the crazy thing is… everything you speak of still exists!

Well, well said. React is an excellent unilateral flow of data (combatting Angular’s two way architecture) but it is only going to cover your view layers. It is meant to be integrated into MV* whatever pattern of choice. I’ve been pairing it on its’ own and with Backbone thus far.

Hugo, what is it that I need to be informed about? I have seen that video, I get what is going on. Here’s a JS fiddle with the code from the video: http://jsfiddle.net/3dd1wh8v/ Play with it, and you’ll see it still suffers from the problem I’m talking about: The meaning of data.message depends on which input you type into first. If the video author missed that, I don’t blame him, because it’s an incredibly stupid and unexpected behavior.

@hugo That glitch is unlikely to happen if you try to initialise any object you will use in your scope and if you only bind to properties of those objects (using controllerAs and only binding to those controller properties will do the trick).

Regardless, It’s something that most new user will stumble on.

Said that, I can’t see an alternative. Directives need to have the the options to either use the parent scope, to create a new scope isolated from its parent or to create a scope that inherit from its parent; a directive attaching new attributes to the scope for its children, like ngRepeat for example, has to make sure it won’t overwrite any attributes of its parent. I wander why ngIf would need to create a new scope however.

Some parts of Angular are extremely appealing. Especially for simple pages where a UI dev is working on it and they have a hard time with more complicated concepts because they don’t have the development background for it. I spent a few weeks with Angular and was not happy with the digest loop and a few other concepts that get pretty nasty with more complex single page applications. Maybe they’ll get things right with 2.0.

Bad Idea #1: Dynamic scoping
Yes. Scope inheritance is funky – especially if you misplace a dot. (Everyone gets burned by that at least once.) IMHO, it should only be used in directives that do transclusion. All other directives should have data injected into an isolate scope. (We use isolate scopes almost exclusively in our apps.)

Even so, we still see $parent show up in our view code from time to time. So yeah, that sucks. 😦

Bad Idea #2: Parameter name based dependency injection
I disagree with this one for two reasons:
1. I’m not sold on the value of variable name mangling. (Just GZIP your HTTP payloads, yo’)
2. There are minification pre-processors that will add in the minification-safe syntax as part of your asset pipeline. Don’t write that stuff by hand.

You’re right about the stupidity of having two minification-safe injection mechanisms. They should kill one off.

Bad Idea #3: The digest loop
This is a hack to overcome the lack of object.observe() in ECMA 5. This will be added in ECMA 6 – it should make this a thing of the past. Hopefully this will also allow Angular play more nicely with non-angular libraries. I think this is in scope for Angular 2.0.

Bad idea #5: Unnecessary complexity
The entire module, dependency, registration system needs to be reworked. Their module system doesn’t scale, and we’ve had to come up with clever workarounds to keep it manageable. (Example: https://github.com/reachlocal/angular-nested-module) I’m interested to see what it looks like in 2.0.
Angular’s injector registration system is tied to the software lifecycle and the type of the thing being registered. Maybe these concerns should be handled more cleanly?
I do agree there is a lot of cruft and they should consider narrowing down the types on the public API. On our projects, we only use a few of the “types”:
– Configs (we use this to configure the app as it’s being bootstrapped)
– Runs (we use these to do last-minute configs after the app is bootstrapped, but before anything loads)
– Factories (we pretty much use this for all our factories, services, values…)
– Controllers and Directives

We don’t use Provider, Value, Constant, Decorator, or Service.

Also, I’m surprised you didn’t call them out for having a Filter called “Filter”. ‘Cause that was a brilliant idea. 😉

Conclusion
If nothing else, Angular has proved there is a place for powerful client applications with full support for 2-way data bindings. When the framework is firing on all cylinders it’s a little crazy how much you can do with very little code. It has its warts, but I haven’t found anything I like better for front end development. Yet.

The comment from damien proves to me that this is not the framework that I want to invest time learning well. I’m an experience JS developer, actually one of the developers of an in-house MV* framework that came out around the same time as Backbone and Knockout. I’ve worked with many web frameworks and lots of JS, both client- and server-side. And yet there is practically no phrase in the following reflection which makes any sense **except in the context of Angular**. You have to drink a lot of Kool-Aid, it seems:

“Said that, I can’t see an alternative. Directives need to have the the options to either use the parent scope, to create a new scope isolated from its parent or to create a scope that inherit from its parent; a directive attaching new attributes to the scope for its children, like ngRepeat for example, has to make sure it won’t overwrite any attributes of its parent. I wander why ngIf would need to create a new scope however.”

Sure, every library has its learning curve, and there is often specialized terminology. But this is overboard, and my guess is that it sounds natural to Angular folks.

#1: Using properties that have not been initialized properly? Come on …
#2: You’d use a preprocessor to annotate your function for correct DI after minification. Pretty straight forward …
#3: ECMA5 has no Object.observe() – that’s a workaround and will be “eliminated” when ECMS6 arrives. I am building quite huge applications with Angular and have not had any major performance problem that couldn’t be sorted out. Especially Angular 1.3 brings with e.g. one-time-binding huge improvements in that area.
#4: Well, this is the only point you are more or less correct.
#5: DI is more than getting an instance of a component. There has to be a way of defining how the components are handled (e.g. singletons or not). Maybe here are some flaws but in general these decisions are utterly right.

Thanks! I’ve tried to put my uneasy feelings around grown-up AngularJS into words but couldn’t clearly pinpoint the root issues. You explain this perfectly clear and well-argumented. AngularJS is great for small apps, but the complexity and hard-to-track-down-issues explode with larger apps.

My company adopted Angular because the managers, without consulting any of the developers, said so. They said that the MEAN stack is the future and that was the end of it. Me and 2 other guys heavily added to the blue line.

@Jos de Jong: i’ve found quite the opposite to true after using angular to build a large scale project. it actually helps with compartmentalising functionalities and defining interfaces between components, limiting maintainability problems in the long run.

Great post! More people should look at all those new toys in more critical ways. Before they are even released in stable, people tend to already get too excited about the few demos on the webpage and talk about it as the future of the web and the most innovative they have ever seen.
Angular does a great job on their website! Every other tool might be better but some framework homepages can be really scary and especially all those “JQuery is a framework”-people (puhaha) are easily caught by fancy demos and examples and with telling them how fast you can get some results, I think that’s where the popularity comes from. It’s a quick and dirty framework for all the novice developers who only want to add some fancy to their website – software/app development with AngularJS is not possible, you would need something more serious like emberJS or meteor.

Although you do make some valid points on your post I wouldn’t agree on all of them and not completely. For instance #1 works the way it does because Javascript as a runtime is dynamic. If you’d write your example like so (http://jsfiddle.net/1op3L9yo/66/), both scopes would have the same properties defined and it would be inherited correctly. But because you don’t, it happens, that inherited controller gets its own property before it’s parent so they’re not the same property any more even though they share the same name. Prototypes man…

But otherwise Angular is not such as bad library as you describe it. It’s actually thinking of the future of script language what others have pointed out so it will just become even more powerful and better in time without you having to change your code. It will just get on steroids so to speak.

Great article, echoes my experiences exactly. Heaven and Earth have been moved to prioritize 2-way binding above all else, ignoring the ramifications of a heavy $digest cycle and constant pinging of the DOM causing layout thrashing and rerenders. While 2-way binding is “neat,” it certainly places a heavy tax on the UI, especially low powered devices and browsers as you’ll find on mobile.

I tried optimizing my Angular app with ng-repeat using track by, ngInfiniteScroll, iScroll, and bindonce but was never able to improve performance on mission critical mobile browsers. ReactJS directly addressed the issues I was having and I am now moving to the Flux architecture, abandoning angular. React instead prioritizes DOM reads/writes, acknowledging that this is the greatest bottleneck in front end UIs. Angular by contrast constantly pings the DOM during its $digest cycle, forcing layout thrashing and repaints.

Angular needs to take DOM render performance seriously if it wishes to maintain its popularity, especially for those concerned with the viability of rich UIs on the mobile web.

I’ve developed 20+ Angular apps over the last couple of years or so, both large and small. None of the issues you mention have even caused a minor concern for me. Either I’m a lucky genius, or you are truly great at finding trouble. 🙂

I would say Angular’s popularity is about 99% about it being a great framework. Sure, not everything is easy to grasp on your first try, but once you gain some skills, almost everything is easy, including creating a highly testable, well structured app, with great performance and super high developer velocity.

How does knockout compare? We have been using Knockout quite a bit and I just started looking into Angularjs. I find knockout quite easy to understand after the brief learning curve with the tutorials. However the documentation still isn’t that good and have to rely on Google a lot.

Although I’m aware of the issues you mentioned I agree with @gunnarlium. And I can’t imagine where you might wonder to put more than 2000 bindings in your view. It’s either bad design of your app or poorly written code. I’ve seen around 1k bindings in a quite large system with map and dynamic layers, heavy data analysis on charts and so forth… And that apparently could be optimized to reach lower number than 1k, but I can’t really see a problem with Angular in your case, rather than problem with app itself.

It’s funny / sad / painful to see the Angular apologists here in the contexts. “You don’t get it. You’re not good enough to understand why Angular is perfect.” Ha! Just like PHP’s defenders. Your criticisms are very valid.

The only valid criticisms in this entire post follow a common theme. One was of the angular documentation and the Angular team’s penchant for repurposing commonly understood terminology to an uncommon meaning without providing any insight into what they do intend it to mean(I don’t like it when they do that anyway. I think it’s stupid and arrogant in a very bad way.) Another is the documentors’ unprofessional approach to doing their job, but that follows what I have seen as an ongoing trend of the developer community equating quirky marketing/documentation with insightful implementations (i.e. “sloppy, colloquial documentation makes a piece of software cool”) The final is in their misuse of terminology: (you are correct in saying that angular does *not* have its own execution context…it’s javascript. The only separation between javascript and angular is conceptual)

To the rest of the post:
Your very first point starts your critique off on very bad footing. By my reading, it seems that you are attempting chicanery with the uneducated or you do not understand the framework well enough to pose this criticism in the manner you have. What you have done by framing your example without explicitly defining your controller is to cause each ng-model declaration at each level of nesting to create an individual scope (with the outer div being the parent scope to the inner, and the inner overwriting its local copy of prop. Had you wrapped that block with and defined the “prop” property in the controller rather than an uninitialized scope obj, your criticism evaporates. The reason the code in your example behaves the way it does is because you did that entirely wrong to create the behavior you expected. The correction is trivial. In general, I think that using ng-model is a stupid practice…I’m sure there’s a “but wait! It lets me do {x}” that makes it useful, but an example does not come to mind. You should define scoped variables in your controller and *reference* them in your view, not define them in your view and attempt to push them into your controller. What you have done is break the SoC Principle and then complain that your concerns aren’t properly separated. The answer to your quandary is “do not do that.”

Regarding criticism #2:
Your evaluation of parameter named based dependency injection as “bad” is entirely subjective and one I consider wrong. When you are injecting a dependency, at some point you have to identify “the thing” you are injecting, and while ‘magic strings’ are inherently buggy regardless of language…they are ‘the best thing’ available in javascript. They also work quite well at getting the job done with simple to perform, good development practices. When attempting to create a new injectible, write your new module’s header, save that file with no implementation details, go to your application root (index.html) and bring the module into scope with a script reference, go to your composition root (appConfig) and push the new module into your app module as a named dependency, reference the dependency in whatever feature file you want to inject it into. Now run. If you get angular injector errors in your console, you have set your dependency up wrong (syntax error, a naming error, or a forgotten step). It seems like a lot of steps when put into text, but it takes less than a minute to set up a new injectible after about a half hour of experience with Angular. My advice is that if you get to your “run test” and things break, spend no more than 5 minutes trying to debug. If you can’t figure it out in 5 minutes, trash your local workspace and create the injectible again (again, it should take about a minute to create.) …and since you sync all of your code with the repository every time you make a change and since your continuous integration processes are running on your build controller and validating your current source repository, you should have no problem starting over. You’ll lose 5 minutes worth of work.

“It’s unclear to me why it is a good idea to have two ways of doing this, and which will win in case you do both.”
Regarding the multiple syntaxes for injection, the var ctrl = angular.controller(‘MyController’,[‘scope’,function($scope){} syntax is for defining a controller; The MyController.$inject[…] syntax is for injecting mocks for testing;MyController.$inject wins. ($inject is also very useful for fiddles and plnkrs)

Regarding criticism #3:
Trivial; it is a workaround for ES5’s lack of object.observe(). This is being taken care of with the upgrade to ES6 with Angular 2.

Regarding criticism #4 (and part of #5):
Duly noted; already acknowledged. They really, really need to stop doing that, and developers in general need to stop sucking at writing documentation. Rule of thumb (Strong Opinion; Loosely Held), if you **ever** speak outside of the third person in formal documentation, you should shoot yourself in the face….twice if necessary. I ****ing hate that!!! Where the **** did you people learn to write!!! GAAAAAHHHHH!!!!!!!

Regarding the part of #5 that I consider an invalid criticism:
Honestly, I’ve ignored most of the angular documentation except for survey information and instead look at what the angular source actually does at execution. Most of your criticisms seem to be about the documentation, but you extend that criticism over the framework as a whole using dubious methods.

One piece of criticism I have for Angular that you didn’t mention (and one I have for software products in general) is the amphibolous usage of the word “Beginner.” I usually consider the word “beginner” to equate with “amateur.” In most actual usage, “beginner” is actually intended to mean “beginning professional.” If you have never seen javascript before, you should not be attempting to learn Angular yet. If you don’t know what the MVC pattern is, you should hold your horses on Angular, wait until you graduate high school, enroll in an accredited university and attend the first week of classes. You’ll learn what the MVC pattern is.

And that reminds me of one of my favorite sayings: “If you think hiring a professional developer is expensive, try hiring an amateur.”

@K. Alan Bates wow that’s an in-depth reply, thanks for your opinion. You suggest that Lars Eidnes may “not understand the framework well enough to pose this criticism”, then you explain what he is doing wrong and how you should do X and Y in Angular. I had a good laugh at that point. Seems you miss one the main points of Lars’ examples, let me quote: “At this point, I imagine some readers are eager to tell me how to avoid the above problem. Indeed, when you know about the problem, you can avoid it. The problem is that a new Angular user likely does not know about the problem, and the default, easiest thing to do leads to problems”. Note that “new Angular user” does not need to mean “amateur”. It could well be a JavaScript guru missing Angular knowledge. Angular requires a lot of Angular-specific expertise to deal with all of its quirks. Still, it’s awesome for rapid prototyping and small applications.

@Luke that’s nonsense. Building (web) applications *is* complicated and *does* require expertise. The frameworks and libraries that we use try to make our life easier by offering models and abstractions. At the same time they introduce overhead, limitations, and new complexities. Some frameworks are simpler to understand than others. Some are more invasive than others. Some offer a more powerful abstraction than others. Some introduce more quirks than others.

@Jos
The point about “avoiding the problem” was understood, but it is an invalid one. Saying that “the simplest, easiest thing to do” does not result in correct behavior is itself silly. Who ever said “the simplest, easiest thing to do” was supposed to be correct? We’re talking about an MV* framework and there was no glue referenced in the view file to attach behavior. Seriously…does anyone expect magic?

What Angular did by allowing that borked markup to attach any behavior at all was that it assumed it meant each level of nesting was to be in its own scope, which is an opinionated stance by Google. But no one ever posed the idea that Angular was an unopinionated framework. My point is, you can write broken code in any language and then complain that the program doesn’t behave as you expect, and I say that if one can’t pick up Angular, hearing that it is an “MV* Framework” and at least wonder where the Controller/ViewModel/Presenter/{Whatever} declaration is supposed to be made and how they are supposed to link that to the view, then they *are* an amateur. Writing amateurish code and saying “see. It doesn’t work” is not treating the subject seriously.

@Jos
I kind of accept the point (while disagreeing) that it could be better if Example #1 resulted in a console error rather than the experienced behavior, but in MVC (assuming the ‘ * ‘ to be a Controller) you have a View, you have a Model, and you have *gasp* a Controller.

If you don’t declare a Controller and you don’t reference a controller in your view, what would you *expect* to happen when you break the most basic software architecture pattern known to man?

I take your feedback to be sincere, so I’ll respond, even though a) there are accusations in your posts that I don’t appreciate, and b) you don’t always address the actual points I make.

Point by point:

#1:
I know you can get predictable behaviour by initializing obj.prop on either of the scopes. This wasn’t a Stack Overflow post. You say: “it meant each level of nesting was to be in its own scope”. This isn’t what happens: The scope of the variable depends on which input you type into first.

If you think this is obvious, and a mistake no one would make, look at the egghead.io video posted above. It makes this exact mistake, in a video that intends to teach people how to use Angular. The person posting the link didn’t understand the behaviour either. In fact several responses to this blog post have failed to understand the behaviour of the code snippet, even though I posted a JSFiddle. This isn’t because the posters are dumb, but because the scoping behaviour is badly designed.

Consider the case of JavaScript’s implicit globals, where forgetting a ‘var’ will introduce a global variable. This is widely considered one of the biggest design mistakes of JS. Angular is making the same class of mistake here by not requiring a variable declaration.

Finally, while I understand that this is unproblematic to you, it seems the Angular team themselves agree that this is poor design, as it looks like it is being removed from Angular 2.0. Look here: https://www.youtube.com/watch?v=gNmWybAyBHI#t=785
Exact quote from Angular committer Tobias Bosch: “You know, this took me a while to figure out when I got started with AngularJS.” Igor Minar: “The good news is […] we no longer need this.”

#2:
This paragraph seems to be an instruction on how to do dependency injection. It doesn’t respond to my actual criticism, which is that one of Angular’s mechanisms for DI breaks under minification.

#3:
Object.observe hasn’t shipped in Firefox, Safari or IE yet, and there is no shipped version of Angular that uses it. It is hardly relevant to the point I make, which is that Angular as it is now relies on a digest loop that gets prohibitively slow under certain use cases.

@Lars, thanks for responding. Most of my posture for my comment comes directly from my interpretation of point #1 (it rubbed me the wrong way for some reason), so I’ll spend a moment to clarify that and try to present my thoughts in as objective of a stance as I can. For what it’s worth, I’m sorry.

The sample app that you contrived to demonstrate the behavior to declare the scoping mechanism ‘insane’ is code that I wouldn’t think would ever work in the first place, at first blush. Even so, it really should come as no surprise that since the inner “obj.prop” inherits from the outer’s prototype, if the outer obj.prop has been initialized, the control on the inside and the control on the outside will be bound to the same “obj.prop” variable. If you change the inner one first, the parent property does not exist to bind to the input so they will be disjoint. Your complaint is with prototypical inheritance, not with angular. To the ‘amateurish’ criticism, I was speaking specifically of the code in that example. It *is* amateurish. The solution is to write a controller and include it in your page. This isn’t some “OMG! I didn’t think of that!! type of insight.” A controller is one of the basic requirements of MVC. If you don’t have a controller married to your view, you are flat out doing it wrong. So…that’s why I thought you may have been conducting a bit of sleight of hand to dubiously undercut a framework you don’t seem to like.

“Consider the case of JavaScript’s implicit globals, where forgetting a ‘var’ will introduce a global variable. This is widely considered one of the biggest design mistakes of JS. Angular is making the same class of mistake here by not requiring a variable declaration.”
Fair enough.

#2 “Even when you’ve banned the use of this DI mechanism [the one that breaks minification] in your project, it can continue to screw you over, because there are third party apps that rely on it. That isn’t an imaginary risk, I’ve experienced it firsthand.”
This part I entirely missed. That is valid criticism; an experience I have never had, but duly noted.

This is honestly pretty brutal. Half the things you mentioned are only a problem within the first week of developing in angular. Did you seriously bring up DI syntax? I currently am working on 4 large angular applications for the same company, with many developers, and I haven’t seen a DI problem ever with or without ng-annotate. Everybody understands this now. Also, the binding? Angular 1.3 with bindonce performs almost identical to any jquery bind or server side render. Check out the news Sears website http://www.sears.com . Great example of fast performing large angular app with 1000’s of bindings.

Listen there are things I don’t like as well, but having used the alternative MVC frameworks (note I said MVC and not framework) there really isn’t a better alternative right now. Knockout can be too minimal and not enough community involvement, and the other corporate choice asp.net is just a pain to work on and find developers for.

huh? I see no evidence of anything in the sears home page, other than a complete and total lack of care regarding using valid html, or sane javascript. I definitely don’t see any Angular, only JS i see it loading at all is jquery and some analytics junk.

@Eric Blade
The criticism of the DI syntax that Lars brings up is valid, although I’m fairly confident that it is a minor concern.

The criticism is that if you are planning on minifying your javascript, you can’t consume external components built from the “basic” injection syntax and expect them to work.
—–

Now…*why* anyone would actually use the basic injection syntax when publishing a component for others to use is beyond me…and why anyone would rail against any development framework for some random developer’s lack of rudimentary understanding of the publication process seems to be misplaced blame.

Yet another oddity of the criticism is that it heavily implies that the author broke a basic rule of thumb with minified javascript: If you plan on minifying, you should only use minified versions of external dependencies; if no minified version is available, you should *expect* that library to be unsuitable for minification.

A further oddity of the criticism is that if you did -in fact- retrieve an unminified external dependency, modifying it for local minification is a fairly trivial exercise. I can understand the frustration of having to modify someone else’s code to make it work out of the box for your use case (I’ve been there; I’ve felt that pain), but if you’re trying to use it in a way that the author did not intend, customization is almost inevitable. If that author lied about their component and said that it had passed a test that it had not passed, then the fault is entirely with them.

I use Angular every day and have come to really love the framework, but I think the biggest design flaw that may have measurable performance implications is the management of 2-way bindings. In my opinion 2-way bindings should be an opt-in instead of the opt-out we have now with :: syntax. Doing it as an opt-in would have given us a built in performance enhancement since a lot of the bindings don’t need to be watched – especially ones that are just part of the initial UI composition.

Yep, there are bad parts, most can be worked around pretty easily, but have certainly lengthened my learning curve unnecessarily. The complexity introduced by the various flavours of provider is really dumb, but it didn’t take long for me to decide to use only the Factory pattern and ignore the meaningless jumble introduced by the rest of the nomenclature.

On balance though, with Angular, this is the first time I have really enjoyed developing with Javascript, particularly because of the ease of testing enabled by the DI. This is the application I have written the most tests for, thanks to Angular. As a result I have a better modularised, better tested and more robust application than any other I’ve worked on with Javascript. In fact, because of the lack of compilation I’ve avoided it for the better part of a decade, now I’m seeing its advantages unleashed as JSLint and Angular + the Karma test tooling provide me with a safety net that allows me to develop with confidence.

I am using Angular in a way that only requires a small number of bindings for a B2C facing website, I suspect the digest cycle overhead would be a bigger issue in internal or B2B applications. With that most fundamental problem excluded (until Angular 2.0 solves it) I don’t find any of the other issues is insurmountable, and none of the other frameworks I evaluated seemed to come anywhere as close in terms of the clarity of code control and testability that Angular makes possible.

I think some people just need something to bitch about. These are minor irritations and can easily be worked around.
Angular solves WAY more problems than it creates but one said it’s perfect. Then again, none of the alternative frameworks are either, so I’d love to know what you’re suggesting we do?

Ya, I concur. Sad to see all of the young monkeys here missing the point completely. Bless. My impression is that the folks drinking the angular kool aid are… a younger crowd? Is Angular the J2EE of the javascript world?

Also +1 for knockout.js. A very focussed and minimal data-binding library. I have worked with it for around 2 years on a large application, and my experiences were very positive. It handles complex data dependencies very well and is intuitive. Anyone here who hasn’t checked it out must do so. AFAIK it beats the pants off all the others 🙂

Interesting to see that we are not alone. Some of the points sound really familiar to me. Well I think/hope that with the continuing development and with the version 2.0 which will hopefully come some day, things will get better. The only thing which is for sure is that people either love or hate AngularJS. There is nothing between. I for my part love it, but that doesn’t mean it loves me too 😦

I am trying to clarify the trade-offs in the following spreadsheet. I invite the author or any readers of this thread to contribute to it. Your help will reduce the struggle faced by all developers in choosing technologies. Send me a message and I will add you to the editor list.

Everyone here has an opinion and many of you make some valid points and address the OP’s objections. However answers like the one provided by @gunnarlium are what matters the most. Ultimately, *that* is what defeats everything else. We can all discuss the dos and don’ts of any given framework and arise our voices claiming that we can make it a whole lot better or add that feature that it was oh-so-unforgettably-not-included; however, in the long run, what makes a web framework (or any framework, for what matters) any good is the ability to create applications, the tools it provides. And that is what people is doing with AngularJS. Is a framework allowing you to build “20+ applications” with no major, breaking problems along the way? Then, I may very well be inclined to say: stick to *that* framework.

PS: @Ahmad, sadly enough, I found your simple comment the most valid response to Bad Idea #1 – such a shame that it went along swiftly unnoticed.

Check out HtmlJs, it could solve all bad designs of AngularJs, of course with a trade-off – longer code.

#1 – HtmlJs has native scope as it only uses JavaScript to bind data
#2 – Obfuscation is as easy as possible, just use any tools.
#3 – No digest loop. HtmlJs uses observer, observer fire subscribed functions after data change, that’s it, no more extra work.
#4 – No new terminologies
#5 – Flat learning curve, in fact, it could only take 30 minutes to master HtmlJs. I’ve created a lot of live examples on the home page, just go there, modify examples without downloading, setting up things, …

angularjs not a pit of success. too complicated. documentation by angular team contradicts best practice commandments by john papa. I tried angular only because Microsoft executives issued statements indicating it’s the next big thing. i’m still learning and not sure if I will give it up. learning knockoutjs for two-way binding was easy. angular is just too complex

Great write up. Just wondering – can you give a breakup of the performance boost you got with each optimization. We’re trying to optimize our Angularjs website and were wondering how we should prioritize these optimizations. Thanks.

Great article. What would say Dijkstra about Angular?
I sometimes wrap small libraries with code that makes them appear elegant in my project(s). Takes less time than writing unit tests but gives even more confidence. Obviously we can’t do that with Angular. It’s so big we’re just supposed to blindly adopt it and let it grow inside our back and front-end. Nope, nope..

Agree on most part of the article but definitely not the final conclusion. I knew all these issues but I still start most projects with AnguarJS just because it allows me to create complex UI with less code. Angular is the best web platform for prototyping ideas.

Finally, there is someone who can see beyond the noise.. We need more persons like you. Everyone is all about the buzz. A ton of companies put it in their stack without knowing the entailments. Thank you for writing this article. I was trying to pick what frontend framework to learn as I am a backend guy. You think this article has solved my problem

This article is full of bad articles with a weak conclusion. None of the 5 ‘bad parts’ are truly shown to be bad. By labeling them with common buzz words that mystify the development experience, you fail to show that the ideas are bad at all. This is just an angsty article that attempts to overlook the advances made by AngularJS. You clearly have an axe to grind based on the prima facie complexity introduced by the Angular framework, but you fail to recognize the reduction of complexity in javascript code brought by the framework.

If I wrote an article about 5 bad parts in Star Wars that burn beginners, I’d see the same negative comments.
– You don’t understand the force. Let me explain.
– A light saber is only hard to use if you make stupid mistakes.
– Sure a light saber is tricky at first, but after you lose a finger or toe, you’ll get the hang of it.
– EVERYONE knows to not argue with a wookie. You didn’t say anything new.
– Just because your shots don’t hit the target doesn’t mean the blaster sights are off.
– Why are you criticizing the Jedi? Life was bad before they came along.
– Light sabers version two don’t have that defect.
– If you’re so smart, why don’t you make you’re own movie.
– Why did you choose the dark side? Overall, the light side is really good.
– Your example is contrived. No one would put an R2 unit in an X-wing that way.
– I’ve been freeing the galaxy for 4 decades, I’ve never run into the problems you described. Either I’m lucky or you’re stupid.

I’d really like it if negative commenters could chill their emotional reaction enough to meet you in your original context. It’s like you said, “The box is red.” and they replied “Purple is stupid.”

I, for one, am no dummy, and I hit the issues you mentioned. I felt setup. I still love Angular, but I’m not going to deny the warts. I don’t like react for its own warts. I hope Angular 2 is better.

Seriously… All frameworks are Good+Bad. Learn them All, use them all, love them all.
Makes no difference. Are you actually upset that a group of people got together and had a go at doing something to make your life easier? If angular is bad (yes, parts of it could be improved), and you know how it should all work, start an open source web framework project and let’s get cracking…
What I want to see stop happening, is destructive thought being thrown around when all we have as developers are good intentions to solve a really messed up problem. Yes, you can investigate, critique and share your results, but drop the negative emotion. Otherwise, I pretty much agree, Angular 1 is dead though, lets up and on to Angular 2…

The problem isn’t that there are many options. That’s great. The problem is that it takes, depending on your expertise, a very very very long time to learn each of them well enough to be able to make an informed decision between them. You may disagree with this statement. That’s fine. It just means you’re brilliant. For the rest of us it takes a very long time to find the gotchas.

I am trying to solve this problem and I need help. I have a database of the selling points of technologies all set up and ready for your help. People can use it to create comparisons between any technologies like the following. All it needs is expertise donors to scan through a list of technology benefits and indicate which ones their technologies offer. Will you help?

I completely agree with you, it takes at least 18 months to learn the gotchas, and, you have to be building something non-trivial to actually need to ask the right questions of the framework. I think there is actually a lot of build-by-numbers going on of simple web apps etc. Which means only a small % of developers run into these issues. Which is why many devs think these frameworks are golden geese.

Anyways, I’m just trying to say, we shouldn’t be emotionally critical of something which is freely provided just to try and help us speed up development.

I would also love to help. I will check out the link, you should be able to see my email from the comment admin so get in touch with any specific details you need looked at and I’ll contribute as much as I can!

After years of jQuery, and trying knockout on 1 toy app, I did 4 years of AngularJS on small projects. It was a love/hate relationship. From what I’d seen, I never would have trusted Angular on a medium to large project. Still, there was no way I was ever going back to pure jQuery.

I never did get as deep an understanding as you. Love your analysis and writing style. Kudos.

I rewrote one small app in React, and then again in Angular 2. Both solved some problems, but made my head hurt in different ways. Mostly, I hated their dogma like React’s “2 way binding is evil” and Angular’s “dependency injection is more important than anything else.” They both do some magical foo that goes beyond JS and then humbly declare that they are just plain JavaScriot.

Then I rewrote the app in Vue.js. I’m never going back. Vue just feels right. It’s like Angular and React had a baby, and the baby is so much cuter than the parents. The documentation is clear, and the learning curve is gentle. I’m back to solving my business problems, and not trying to satisfy a mysterious, benevolent, but punishing, compiler.

I’m using Bootstrap for UI. It’s easy to wire in with Vue, but it feels klunky. If I can find the right CSS framework built with Vue in mind, I’d reach Nirvana.