
Some AngularJS pitfalls - shawndumas
http://branchandbound.net/blog/web/2013/08/some-angularjs-pitfalls/
======
KeyBoardG
Thank you so much for writing this. My company is planning a move from Fat-
client + in house server model to web based likely a SPA solution.

I've spent the entire weekend pouring over JS frameworks and have had a rough
time trying to compare Angular vs Ember vs Knockout. From a newby, they all
seem to be very similar without having written large systems with each to
compare.

------
beefsack
Some suggestions from someone using AngularJS in a significant sized
commercial project, which manages pages with complex DOM structures with tens
of thousands of nodes on some pages:

* Don't modify the DOM with anything other than Angular, not with jQuery, not with anything. You will create a maintenance hell for yourself if you try to do things Angular isn't good at, but if you work well within it's strong areas you will achieve bliss.

* Make everything a directive if possible, Angular performs much better when you are dealing with much smaller scopes contained in directives.

* Angular watchers (including ng-repeat) can be very slow when handling large complex data sets. Create a $watch on the set length, and use that to create a flat index and ng-repeat over the index.

------
Oculus
Sometimes the drawbacks of a framework are more important than the features.
Most competing frameworks (i.e. AngularJS, Ember.js, Knockout) keep on par
with one another when it comes to features. Their real differences comes out
in the drawbacks of how those features were implemented and whether you're
willing to deal with those certain drawbacks.

------
marquis
Another good reason to use ng-bind is that you have your UI ready to
localization and could save a lot of work later on. We use a combination of
ng-bind and ng-cloak, where the first page view is fairly light so the loading
time is insignificant. It's also an easy matter to write your own Loading
directive which could pop up a loading notification modal for example, though
I personally tend to hate those - I'd rather have a light page view that shows
me something to start looking at and have the main data lazy-load in the
background.

------
greaterweb
Nice writeup, here is my input on the points OP makes.

 _The flickering UI_

A quick fix would be to extract the problematic markup, place in a separate
template file and leverage ngInclude[1].

If you find yourself having many instances where this is a problem, chances
are you aren't doing a good enough job of breaking up your application into
controllers, page components, directives, etc.

 _jQuery and Angular_

In my opinion the example is little weak in terms of demonstrating a pitfall
of AngularJS.

Based on AngularJS best practices you'll find yourself only doing any sort of
DOM manipulation in the context of a directive (much like your example).
Bringing jQuery into your Angular app for the sake of using .hide here
probably won't make much sense. Chances are pretty good you'll leveraging
directives such as ngShow[2], ngHide[3], ngIf[4] (version 1.1.5). No need to
bring jQuery in for that.

If there is indeed a valid reason to bring jQuery into the project, I'd say
the responsibility lies more so on the developer ensuring the dependency is
met.

 _Minification_

With the addition of ngmin[5], this isn't necessarily a pitfall just something
to consider in your workflow. There is a ngmin grunt task[6] that works as
advertised! I've been using it for quite some time now and never an issue.

My thought overall though is if you've conceded minification is a step in your
project workflow, you'll be using tooling to make that easier. Bring ngmin
into the mix and all is well.

 _Directives are never 'done'_

I would suggest looking into using $watch[7] within the scope of that
directive or making use of compile post linking over the linking used in the
example. Check out the compile section [8] of the directive guide for more
information there.

[1]
[http://docs.angularjs.org/api/ng.directive:ngInclude](http://docs.angularjs.org/api/ng.directive:ngInclude)

[2]
[http://docs.angularjs.org/api/ng.directive:ngShow](http://docs.angularjs.org/api/ng.directive:ngShow)

[3]
[http://docs.angularjs.org/api/ng.directive:ngHide](http://docs.angularjs.org/api/ng.directive:ngHide)

[4]
[http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngIf](http://code.angularjs.org/1.1.5/docs/api/ng.directive:ngIf)

[5] [https://github.com/btford/ngmin](https://github.com/btford/ngmin)

[6] [https://github.com/btford/grunt-ngmin](https://github.com/btford/grunt-
ngmin)

[7]
[http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch](http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch)

[8]
[http://docs.angularjs.org/guide/directive](http://docs.angularjs.org/guide/directive)

------
avolcano
> Another option is to completely hide elements, or even your whole
> application, until Angular is ready. Angular provides ng-cloak to this end.

My issue with this is that it doesn't solve the problem of binding to
asynchronously-loaded resources. You have to manually implement your own ng-
cloak logic, or use ng-bind, to not have {{mustaches}} waiting for data to be
loaded.

Compare to Ember, where {{mustache'd}} data-bound bits aren't rendered until
their bound variable is defined.

(the, other fix, of course, is to do something like `<span ng-hide="foo !=
undefined">{{foo}}</span>`, but that doesn't scale particularly well)

~~~
oinksoft
You are somewhat misinformed: If you are using more than one template, this is
a non-issue. Only the HTML from the initial page load will ever display
template bindings, that being the purpose of ng-cloak, ngBind, etc.
$templateCache can be pre-loaded with something like the ng-templates Grunt
task, so excessive HTTP requests are not an issue either.

------
marknutter
For Minification, Brian Ford's ngmin grunt task works perfectly. The OP says
they haven't tried it which to me suggests that they may not be using grunt at
all, which is a mistake. Grunt takes care of a lot of workflow and deployment
details, including minification, and should be part of every javascript
developers toolset regardless of whether or not they use Angular.js.

For the "Directives are never 'done'" issue you should be able to attach any
jQuery plugins to the DOM in question inside the directive compile function.

~~~
SanderMak
Correct, the particular system I encountered this minification issue has an
Ant-based build (which calls r.js for concatenation and minification).

I'll have to check on your compile suggestion. I tried lots of things,
including using the postLink etc. Do you have an example?

~~~
marknutter
Here's an example using the jQuery UI draggable plugin:
[http://jsfiddle.net/marknutter/hSbjX/4/](http://jsfiddle.net/marknutter/hSbjX/4/)

~~~
SanderMak
Thanks! I also just found out about angular-ui-utils, where they have a
generic pass-thru directive for initializing jQuery plugins. Interestingly,
they opted to use $timeout inside the compile function:
[https://github.com/angular-ui/ui-
utils/blob/master/modules/j...](https://github.com/angular-ui/ui-
utils/blob/master/modules/jq/jq.js)

Wonder why that is, are there cases that would otherwise break?

~~~
marknutter
I'm not sure, that's a good question.

------
sequoia
Nice writeup! I'm using angular now on a work project for the first time.

My observation here is the same as what I've had for many "look how easy it
is!" frameworks: A lot of frameworks sport "look how easy it is!" syntax, but
using the easy/short syntax actually isn't adequate in some (sometimes most)
circumstances, and often it isn't even "recommended." So you start using the
longer, more explicit syntax, and all those short/sweet syntax features are
out the window.

For example, the easy-to-read {{obj.prop}} data-binding syntax is basically
dis-recommended here with three workarounds given (use ng-cloak, declare
databindings on element attributes, externalize & `include` the offending
markup). Once you have 3 different workarounds for what's basically the most
fundamental framework feature, I can't help but wonder "why offer this
short/sweet/brittle syntax in the first place? Most 'power users' can't really
use it." The dep injection one in particular- basically any shop worth its
salt is going to use a minifier on big JS applications- none of them (us) can
use the short dep injection syntax. Why even teach it/include it at all?

I guess it gets people up & running faster, but eventually you have to deal
with these pitfalls.

~~~
oinksoft

      Why even teach it/include it at all?
    

I fear that other developers will read a comment like this three months down
the road and take it hook, line, and sinker. It is in line with the narrative
that AngularJS and similar tools hide their complexity/inflexibility, and that
the up-front examples are marketing fluff. I know I am guilty of this when I
look to comments like this one for the "on the ground" perspective.

Real-world AngularJS applications use multiple templates and {{ o.p }}. {{ o.p
}} is the recommended way to render scope 99% of the time.

ngmin[1] solves the DI minification/annotation problem transparently and is
trivial to integrate into any existing build process.

[1] [https://github.com/btford/ngmin](https://github.com/btford/ngmin)

~~~
sequoia

        take it hook, line, and sinker.

:( I'm not a framework developer at all, just a user; this is just my
observation. Maybe it's incorrect or poorly informed, just how it appears to
one person. Anyway I'm not trying to sell anyone anything, neither am I a
fisher of men, so feel free to spit out my tackle. :)

~~~
oinksoft
Perhaps then the lure leads to a tree branch, snarled by a poor cast from
another angler earlier :) I didn't mean to imply devious presentation on your
part.

------
TeamMCS
My biggest issue with AngularJS is the inflexibility of it to play nice with
existing '1.0' sites.

Almost all the sites I'll develop take advantage of progressively enhanced
components or at minimum, widgets that are bootstrapped server side. Angular
wants you to load everything via AJAX - that can get heavy very quickly.

This is also evident within the codebase. You find yourself dancing around DRY
violations as you pass models to your markup rendered server side and also
serve the same/similar content to Angular via JSON.

Now this aside, Angular wouldn't require much work to let it play an
enhancement or migration role. You can already see Google have implement their
own 'private' method on the homepage of Angular.

Another problem that grates me about Angular is dynamic routing and loading
additional Javascript on the fly. I was recently building a portlet based
site. The intention was to make each module entirely configurable by other
developers - that meant passing control to their controllers and allow them to
enhance routing. As it stands you have to do it declaratively or using some
nasty hacks.

~~~
avolcano
> Almost all the sites I'll develop take advantage of progressively enhanced
> components or at minimum, widgets that are bootstrapped server side. Angular
> wants you to load everything via AJAX - that can get heavy very quickly.

I'm surprised that I haven't seen a library for any JavaScript framework that
elegantly handles loading JSON returned within a server-side template. It
seems like the best of both worlds - use your server-side template, but then
also get the data as JSON so that you don't have to do a second request to
populate your Angular $scope with data.

The easy way to implement this would be a library that automatically added
<script> tags containing the JSON that you could then reference from your
Angular template. It could even wrap the JSON in an Angular module of some
sort to keep your global namespace from being polluted.

~~~
SanderMak
Agreed. I tried to explore this issue a while back:
[http://branchandbound.net/blog/web/2012/11/unify-server-
side...](http://branchandbound.net/blog/web/2012/11/unify-server-side-client-
side-rendering-embedding-json/)

Turns out that using script-tags is somewhat problematic, but as a comment
points out, data-attributes could work.

~~~
esailija
This is not a problem in e.g. PHP, out of the box it can embed json in script
tags, with default settings you never get the sequence `</script>` (you get
`<\/script>` instead) and it even escapes U+2028` and U+2029 which are syntax
errors in Javascript string literals but not in JSON.

