Mistakes AngularJS Developers Make
airpair.com
airpair.com
#3: The fact that the default dependency injection mechanism breaks when minified means it should never have been included. If this mechanism sneaks in anywhere in your app (and there are plenty of third party libs that use it), it wont start breaking until you bundle and minify the code, at which point you may already be in production.
#5: The service/factory distinction adds a conceptual complexity that is completely unnecessary, as demonstrated in this blog post.
#7: Having too many watchers will slow your app down to a crawl on a desktop machine, and be even worse on a phone. This happens more easily than you'd think and it's fundamentally caused by Angulars reliance on its digest loop. This loop is the essence of how Angular works, and it means that there are tons of applications that cannot ever run efficiently if they're built on Angular.
8#: Because of the way their $scope system works, it is actually literally impossible to tell what the the meaning of ng-model="foo" is by reading the program. It may in fact depend on the order in which the user interacts with elements of the web page. Consider this example: http://jsfiddle.net/7kkxLkxh/ What foo binds to is dependent on which input you type into first. Yes, there are ways to avoid this, but it should have been avoided by either disallowing this construct, or requiring foo to be declared before it is used. (If you miss a var in JS, you get a global - that turned out to be bad language design. Removing the var keyword and effectively deciding on variable scope at runtime is certainly a much worse language design.)
After working on a large Angular project, it is clear to me that there are a lot of ideas in there that are frankly not good. It's sad that there seems to be very little discussion anywhere about the downsides of the various frameworks that are out there.
I think the real mistake in that scenario is not minifying the code until "in production". You should minify your code from the day one.
Although I agree that the "not safe for minifying" mechanism shouldn't have been included in AngularJS.
On the other (other) hand, it's pretty abhorrent that minifying is something that developers need to actually place any thought into..
How are you debugging minified code?
For #5 the real deal is the provider and everything else is syntactic sugar on top of it. Once you have a very large application you start to appreciate the distinction. The naming of concepts is awful though; could have been much more clearer.
I'm sorry to say it's pretty "retarded" to add in framework-specific minifiers to the build process. I don't want to tweak 10 different minifiers if I'm using 10 different libraries in my application.
Google has released a recommended app structure similar to this as well [0], though I haven't actually seen it being used too often in the projects I've come across.
[0] - https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDz...
Furthermore most likely you will be sharing many partial templates or widgets between different views and sections in your app. So those have to go in some sort of common views folder, losing much of your grouping.
I wouldn't say either way is right or wrong, it just depends on your workflow, size of the app and size of the team working on it.
It's easier instead to just dump all of them into one place and copy that folder, no need to update references etc. then.
Any ideas on how to solve this?
There might be some way to load them into $templateCache on build, but that would kill lazy loading
#1 isn't a "mistake" it's a preference, really. Also, I'm really not sure about putting the template files in the directory with the other files. Then I guess I'd have to add something to my build script to copy them to a hosted directory?
#3.1 IMO, Is actually more of a mistake. Imagine you have 3-4 modules, which one do you define that underscore service in? Oops, better make a 5th "common" module, I guess. Just to include something that you could have injected with $window. Notably, the underscore service is also missing that it should be injecting $window. How is he going to test that? Scrap the whole underscore service, and just inject $window where you need something from the global scope.
#9.1 Protractor - Protractor is great. But it's a little hefty for true "unit" tests. Jasmine with `angular-mocks` works a little better for unit testing Controllers and Services, where Protractor is better at testing directives and full end-to-end tests.
and one thing I'd add to this list:
#11 - Overly "DRY" Jasmine tests.
A lot of Angular developers I know are way too fond of drying up their Jasmine tests with nested describe() blocks and lots of beforeEach() clauses. Tests should really be "DAMP" not "DRY".
Agreed. I've gone down that rabbit-hole myself more times than I'd like to admit. It's very easy to waste time DRYing up your test suite with beforeEach calls; there is a kind of nagging temptation to reduce every spec down to the minimum number of lines required, and it's a pretty cathartic way to avoid doing real work (especially if you're taking a TDD approach), but ultimately, you just end up constricting your test suite in a way that wastes even more time when the day comes that you have to adjust a few specs in a manner which is incompatible with the beforeEach templates you've established for the suite.
Nowadays, as a rule of thumb, if the tests are already passing, I forbid myself from refactoring any specs that test an unshipped feature. Maybe I should be a full time test engineer because it's really not a very healthy behavior. :)
https://twitter.com/sandimetz/status/514411916480249856
I've also heard it expressed as "the pain caused by code duplication is nothing compared to the pain caused by wrong abstractions".
https://stackoverflow.com/questions/6453235/what-does-damp-n...
"Don't repeat yourself (As Much as Possible)"
If there's a complex jQuery widget (say, a date selector) that works well for you, then wrap it in a simple directive and be done.
I've done this a number of times with no problem.
It doesn't work for everything, though, so suddenly it blows up. So if you want to be 100% sure your code works as intended do it manually.
[0]: https://github.com/olov/ng-annotate#explicit-annotations
Also: For situations where you don't know whether ng-annotate will detect a form or not (assuming you already use ng-annotate for your project), you can use explicitly use /* @ngInject */ or ngInject() to avoid stuttering the array yourself.
I have seen people make Angular charts, and they use deep value type watches that kill performance. It is fine for angular to watch a massive array with time series data, but use a reference watch, and swap out the array when the data changes.
People talk about the scalability of Angular's dirty checking, but in my experience the number of watches should be roughly the number of interactive elements in the user interface. Since there are limits to how much interface a human brain can process, the theoretical problems of dirty checking are not real problems in practice. If you have more than 2000 interactive elements on a page, then you probably have a different problem, not an angular problem.
What is happening in the first example is that the ng-bind directive is creating a new entry on the child scope, called "user", which overwrites the reference to the property defined in the parent scope. When you ng-bind an attribute of an object, your child scope is not changing the reference to the original object, so both scopes will show updates.
Example fiddle: http://jsfiddle.net/02f4o7u9/1/
Thanks for the example!
I can't find the exact article I read where the authors discussed this, but I did find a presentation by Brian Ford[1] that touches on the subject.
[1]https://docs.google.com/presentation/d/1Gv-dvU-yy6WY7SiNJ9QR...
It is not an MVC framework. The angular team even describes it as an MVW framework (the W stands for Whatever).
Angular on its own does not provide enough structure to work on large projects. It requires a better structure than using directories called 'controllers', 'services' and 'directives'. These are just angular concepts that do not translate well into understanding of what functionality should go in each directory. If you've worked on a larger project, you may have already seen this problem.
Angular documentation is not very helpful in pushing this point to the developer, however. It recommends[1] the use of 'services' as the place to do all business logic, which causes many projects to put most of the code in the 'services' directory.
To work on larger projects, you should create the MVC structure to fill in the gaps that angular does not provide. Creating top level folders such as: Models, View Controllers, Controllers, Endpoints (server communication). And using services, not as a top level folder, but as a way to instantiate components.
Every time I see it I just wonder aloud, what problem did you think this solved?
In my own experience, I've found taking such a modular approach too early on can actually hinder productivity as I find myself spending too much time on boilerplate and figuring out if, for example, this service REALLY belongs with feature X or feature Y.
I look at them as a way to extract those building blocks that support the business logic in your app such as a data store or driving app state through routes (whether you roll out your own or use third party libraries). Even if you rely on third party libraries to fill those gaps, I like having my controllers/services/directives not to depend directly on them. For example, in the last year I've transitioned from ng-routes, to ui-router and finally settled with dotJEM/angular-routing [1]. Something similar happened when using $resource, then switched to Restangular, to finally use a thin data store on top of $http [2].
Having that code in a separate layer helped identifying service boundaries within the app and made it easier to replace them when needed.
[1] https://github.com/dotJEM/angular-routing
[2] Something similar to the following implementation: https://github.com/vicentereig/hola-apps/blob/master/app/ass...
The author mentions nothing of protractor's use of selenium webdriver and how it does blackbox testing of your app. There is no mention of Karma interacting with your app's code for integration tests, as well. They talk about unit tests under the "Protractor" header, but integration tests can be run both by Karma and Protractor.
9.2: "Once integration tests have been written using Protractor?" Karma and Protractor are both test runners that support tests written for Jasmine (among other various JS testing frameworks). Perhaps the author meant to say "Once integration tests have been written using Jasmine."
Also, I fail to see how, "Waiting for tests to run, especially integration tests, can be frustrating for developers" has anything to do with Karma.
The rest of the stuff was useful, but the author clearly has never even coherently used these testing tools.
The reason I claimed it was a mistake for angularJS devs was that angular seed and yeoman lead to doing directory by type, which I think becomes a burden as an app scales.
There might be a lot of reasons to use dir-by-feature or dir-by-function but if the above is a problem you should probably get a better editor.
EDIT: or use yours better.
I really enjoy 1, 9 and 10. Learn something new everyday.