Lessons Learned: A Year with a Large AngularJS Project
joelhooks.com
joelhooks.com
You will still use all your JS tools, it is your server tools that will get lighter-weight as page flow and layout are (exclusively sometimes) moved to the client. Ideally your server logic returns only JSON and otherwise your server is a file host for HTML, JS, CSS.
Edit: my comment is in response to some other comments I've seen, not the OP.
edit to add: In fact, on further reflection, Angular passes JQuery or JQLite objects to the Link functions of custom directives, so it's actually kind of opinionated in favor of using JQuery, it just adds some extra layers of abstraction to ease some things.
I don't use any jQuery transitions / animations. Angular's own $http service does all the Ajax I need. YMMV, but I like the fact that I can avoid library bloat by learning one useful toolkit very well, and applying it effectively...and I happen to like all of its other features.
in any case, yeah, one way or another, serve static files & expose a rest api. this is the way forward. i'm excited too, just not exclamation point excited. or, well... fine: !
Doing things this way is quite nice, and forces separation between the client application and the server application. It makes the web application just a client, on an equal footing with any other client (iOS app, Android app, desktop application etc).
At https://starthq.com we started out as a single page app with a RESTful API using Angular, but with time it has become apparent that there are certain public pages that need to have the HTML generated server side. Now we have a hybrid solution, where some parts of the page are generated on the server and others, namely the parts that are different for anonymous and logged in users, on the client.
We've experimented with the likes of https://github.com/apiengine/seoserver but the truth of the matter is it's actually easier to generate the HTML server side than using Angular.
<a href="{{page.url}}">{{page.shortName}}</a>
go out on the wire to be uselessly displayed by arbitrary clients (even ordinary browsers that don't blindly trust your scripts) is a really severe failure mode.Just finished building a personal angular web app (http://pokerstoker.com). Generally a pleasant experience! I found the incompatibilities with other libraries (e.g. jQuery) a bit frustrating though.
Show me any code that cannot be written faster and shorter using only jQuery, Underscore templates and a CommonJS compiler. Does not exist. You are writing enormous amounts of glue code to please AngularJS and getting lost in your obsession with tools, imagined reusability and over-complicated testing procedures.
If you are going to be doing something a lot (capitalizing the first letter in your example), then I think writing the filter does save you in the long run. Writing the filter doesn't seem worth it when you project is small. When you project gets big, and there are little one off jQuery statements everywhere, it gets unmanageable.
The filter allows you to look at your template and immediately know why that word is being capitalized. With jQuery, a year from now you won't remember why/how it is being capitalized, and you'll be grepping for ids and class names.
Of course, we're talking about some size of application here. If you just want to make a todo list, jQuery is fine. But when dealing with a potentially huge codebase, it is good to enforce practices.
Furthermore, my experience has been the opposite of yours - I find myself writing far less glue/boilerplate code with angular.
Please educate yourself on the way discussions are conducted here.
You of course don't have to throw everything into directives but then you risk making your code less portable and your tests more brittle. Wrapping a jQuery plugin in a directive is so trivial it's not even worth mentioning, and it certainly doesn't result in "enormous amounts of glue code to please AngularJS".
> over-complicated testing procedures
And this is where you showed your hand. You can't have written tests for an angular app because if you had you wouldn't have made such a bizarre statement. Angular's dependency injection combined with the fantastic Karma test runner makes testing an absolute breeze.
If it is so trivial then it 'is' worth mentioning. Give an example or reference. Personally I like Angular, but I'm learning and finding articles like the OP's are great to read to have others' perspectives on how to work with it.
Where do I start and why is it better because of DI? Also Karma still requires something like Jasmine which means I need to learn two new tools (as well as how testing works in Angular). It is complicated for someone who is not used to testing at all.
Here's an example from Miško Hevery's session on Angular.js at Google I/O:
https://github.com/mhevery/ng-google-io/blob/demo/app/script...
He displayed the equivalent jQuery code side-by-side and the difference was dramatic (spoiler: jQuery was more code).
Obviously jQuery/Backbone setups have their place, but to claim that AngularJS makes you less effective is absurd.
For example, I'm at the 50k mark on the frontend and I found angular's basic building blocks to be too restrictive in terms of organizing things into reusable components. I had to answer questions like "can views do I/O", "does a view own his model", "how deeply should views nest", "should my models be tree-like or rectangular", "can my contracted designer write arbitrary markup with arbitrary UI widgets or does the framework not like that". Angular's way of doing controllers with dependency injection got in my way of answering these questions. I would love to read someone's opinions on this, especially if I'm wrong, but nobody (including me) has time to write a well thought out essay on this.
http://www.dwheeler.com/sloccount/
Both are in all the major repos -- apt, yum, ports, brew etc.
I'd love to hear more about the problems/questions you are having. Have you found answers to some? Here's what I have come up with. How does it compare to what you are doing?
"can views do I/O" I've put file I/O in a controller. If I would have needed to share it across multiple pages, then I would have put it in a service
"does a view own his model" I have model classes defined outside of Angular. Services access my API to create new objects (of types defined by my models). Controllers request data from the services. The service figures out if it already has the data or if it needs to call the API.
"how deeply should views nest" I often wonder how much I should be breaking up my views into smaller view. For the moment, I only create "subviews" if I am going to reuse the subview in another view. This has lead to some large template files. I may need to reconsider if they get much bigger.
"should my models be tree-like or rectangular" I don't understand what you are asking. What's the difference?
"can my contracted designer write arbitrary markup with arbitrary UI widgets or does the framework not like that" Again, I'm not sure what you are getting at. Arbitrary markup and UI widgets as apposed to what?
There's so much I love about libraries like angular and ember, but I'd love it if someone was working on a way to allow additional templating languages. I don't know how it would work, but you could possible compile other templates down to angular templates.
I've been thinking of a way to convert simple templates in angular's style, so that you can do all the cool auto-updating features. If there was a standard for this, templates like mustache, etc could compile a subset down to it.
Disclaimer: I'm the creator of nunjucks https://github.com/jlongster/nunjucks.
For the type of app I've been working on, the all-or-nothing hasn't been a real hindrance, but Angular is definitely a Framework and not so much a modular toolkit (like Backbone, for instance, which I would say is generally a better choice if you want to build something "custom")
This is a major reason why I am coming around to Angular. Take away jQuery and developers have to stop and think. It makes it difficult to rush ahead chaining dozens of callbacks and tightly coupling their code to the DOM, in the familiar jQuery way. This, above all else, is what allows you to create a single page web application. The rest is just gravy on top.
myModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('START');
$interpolateProvider.endSymbol('STOP');
});
However, you should be very cautious with mixing server-side and client-side templating. See:I can't speak from any experience with angular, so this is about all I can say. I think it's great for one-page apps, but otherwise I enjoy writing templates without thinking too much if they are rendered server-side or client-side.
<li ng-repeat="friend in friends">
{{friend.name}} who is {{friend.age}} years old.
</li>
It's the ng-directive, and it's just one example of why directives are so awesome and powerful:I enjoy writing templates without thinking too much if they are rendered server-side or client-side.
Funny, I've been considering this architecture:
client :: js + your nunjucks library for templating
server :: python + jinja2 for templating
Are nunjucks and jinja2 pretty compatible now, to the point that you're actually sharing templates across client and server? The docs currently discourage it:http://nunjucks.jlongster.com/#Can-I-share-templates-between...
It is mostly feature-compatible, but there definitely differences, and the fireplace project installs a compatibility layer. There are minor things, like `True` is `true` in nunjucks, and arrays have different methods. Also, all filters/extensions must be written both in node and Python.
You can go very far though. I'm considering adopting this compatibility layer and putting it behind a flag. It takes some work, but you can definitely do it.
Food for thought: jinja2.meta exposes the AST after parsing the template. Could you use this to do Angular style data binding?
env = jinja2.Environment()
ast = env.parse(...)
vars = jinja2.meta.find_undeclared_variables(ast)
Now you've got a list of every part of the data model referenced by this template. Unfortunately this doesn't give you byte ranges for each reference, but that could be changed.Angular propagates changes to your data model to only the parts of the DOM that depend on it, which is cool. Imagine if you could do the same against a nunjucks template. Given the above kind of dependency analysis, I don't see why not.
Maybe rendering snippets of a jinja template at runtime is problematic?
Aside: yikes on the string-based codegen here...I bet you wish you were still writing scheme ;)
https://github.com/jlongster/nunjucks/blob/master/src/compil...
If you embrace that idea, and go with the flow, you can do some amazing things amazingly fast with angular that doesn't work so well in other frameworks.
ng-repeat, and other powerful directives are what's great about Angular. Instead of writing code to loop through something, you declare that it should be repeated. Templates are much easier to read this way.
Directives change you way you view your markup. Instead of writing a script that generates the markup you want, you are using a new, more powerful markup language that knows about repeated elements (among other things).
And when the data changes, the webpage just updates, with no thought required by you. Recently, I have started using Firebase, which makes this even more powerful (any realtime updating database library would do the same). I just have to say where in Firebase I want to fetch the data from, and where that data goes in the template. From then on, my webpage stays up to date as the data in the database changes (regardless of who changes it).
Knockout is great, but the two-way bindings do come with a non-trivial performance impact.
Knockout basically thoroughly entangles itself with my model -- I have to wrap my attributes in ko.computed() or ko.observable().
That makes unit testing messier and leaves me constantly trying to remember what is left as .dataAtttribute and what's been transformed into .dataAttribute().
I have to declare the bindings in Angular also, but after that it largely leaves my code alone. I shaved about 10% off my model code when I switched.
In a recent rewrite of one of our applications into Angular we had huge issues with it consuming memory. I think our use case is quite distinct, we have a telephony component that needs to stay loaded so single page app really does mean single page app for us, but even so I would expect to see memory mentioned every now and then.
That isn't to say you still can't shoot yourself in the foot with Angular (especially with something like ng-repeat). There are ways to code an Angular app with an eye on memory / performance, and there are ways to do the opposite, but I don't feel like the framework itself introduces significant overhead.
In the end we looked at the bottom line memory consumption and experimented until we saw reductions. We found using things like ng-show instead of ui-if, essentially preloading the partials and switching between them instead of reloading everytime, saved us enough memory to make the system viable.
However, at Google I/O they demoed the new object allocation tracker, which seems like a vast, vast improvement. Highly recommended for figuring out where memory is leaking and what code is causing it.
Here's the session: https://www.youtube.com/watch?feature=player_embedded&v=...
Still pretty primitive, but progress at least.
Get through to where you pick your cake design and pick one, then go back, repeat and watch the memory increase.
Keep your controllers lean and mean. Put more of the UI logic into services. This makes testing a lot easier.
Only use routing ($route, $routeProvider) if your app has very little UI state and could reasonably thought of as several totally independent pages. I switched to managing $location.path() explicitly and haven't looked back.
Embrace promises throughout your app. Angular templates can now render promises naturally but I've found that I prefer to be more explicit:
$http.get(url).then(function(response) {
$scope.something = response.data;
}, function(error) {
$scope.$emit('error', error);
});
Wrapping jquery widgets in directives often results in more code than just doing it yourself. Obviously this increases your maintenance surface area but typically less than you would expect.The Angular Bootstrap project is trying to create directives that avoid jquery and could be bound (with different templates) to other UI frameworks. What I really want is a core set of UI widget directives and then separate (bootstrap or foundation inspired or not) CSS libraries for styling them.
We actually use a Command implementation that I ported, which has been really handy for leaning up the controllers, plus they aren't stateful so they are always available. https://github.com/joelhooks/js-command-center.
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
// with correct but slower alternatives.
if ('i' !== 'I'.toLowerCase()) {
lowercase = manualLowercase;
uppercase = manualUppercase;
}
Looks like Angular is more battle-tested than I would expect..http://www.i18nguy.com/unicode/turkish-i18n.html
So basically if your goal is for "i".upperCase() to produce something which goes to another program (e.g. an API call), the Angular code is correct. If it's going to be displayed to humans, it's wrong. The documentation at http://docs.angularjs.org/api/angular.uppercase should probably specify that this cannot be used for user content.
This seems to be yet another one!
I would add that the Yeoman build manager is an absolute delight to structuring and managing your project. It was so slick that when making my first Angular app, I also ended up learning CoffeeScript to kill two birds at once...Yeoman made this otherwise prickly situation as smooth as possible.
And I also would agree that directives are Angular's killer feature...and unfortunately for me at the time, one of the hardest to grok because of their magic. Anyone writing an Angular tutorial would do a great service to emphasize the power and use cases for directives.
two places really helped me go from wtf to, "hey I can do that!" (and now I'm onto transclusions)
http://www.egghead.io as mentioned
and the Angular meetup video about directives: http://www.youtube.com/watch?v=WqmeI5fZcho
The documentation for Angular is dense. As in I've found myself having a lot of "ahah!" moments from reading 3 words.
I'm actually starting to annotate it on my blog as I find stuff out.
All that being said, I'm using Angular to make single page app prototype very quickly, coming from having no experience creating single page apps, but with a fair few years of MVC under my belt.
http://thesmithfam.org/blog/2012/12/17/communicating-between...
1. For CSS, honestly for any project it'd be hard for me not to simply use Bootstrap and be done with it and then do the minimum CSS possible (I say this as someone who has been doing CSS for years). Bootstrap really is the de facto CSS for the modern Web;
2. Organization: I agree the angular-seed project (which I started with and still use) isn't suited to organizing large projects and I agree separating into functional modules is the way to go;
3. Directives are awesome but to get all the two way data binding working requires a fairly deep understanding of how they work (to get the correct combination of scopes, transclusion, etc);
4. Directives also have limits that are sometimes annoying. You still can't generate definition list items with an ng-repeat, for example (a template has to have one top-level element). A solution to this is coming;
5. There's no good way of both just including code and using the parent scope plus some extras. This is done for reasons of code separation but there really is a use case (IMHO) for includes vs imports for templates;
6. $resource has no HTTP PUT method;
7. I'd probably avoid $resource altogether although it looks attractive. Use rich objects [2] instead;
8. Angular allows you to use a bunch of different other frameworks but it doesn't always play completely nice with them. Take the popover in Bootstrap. The way this works is by moving an element around in the DOM. This doesn't lend itself to a "nice" Angular directive (my version at least is kind of a hack).
9. Underscore.js is awesome. Use it;
10. Try to limit yourself to using jqLite that Angular comes with rather than full jQuery. This isn't really possibly with any reasonable part of Bootstrap however;
11. Isolate scopes in directives results some weird and unexpected behaviour. For example, if you reference something with an attribute, it'll work if it's an object (including an array) but won't with a simple value, requiring you to put such values in objects just so the directive can get a reference to it. This tripped me up a few times;
12. Scopes aren't always truly isolated either. For example, you can get tripped up by this if you have two attribute directives on the same element.
[1]: for those who follow IO, I am the Tech Lead of the Open Bidder project at Google (http://googleadsdeveloper.blogspot.com/2013/05/announcing-op...)
10. Try to limit yourself to using jqLite that Angular comes with rather than full jQuery. This isn't really possibly with any reasonable part of Bootstrap however;
Can you explain this better?Because angular already provides you $http and $resource which are more than capable of doing everything that $.ajax does - $.ajax and all its variant that JQuery brings in become redundant.
Because of the way Angular is and because of its philosophy of directives - You generally dont need to query the DOM at all most of the time ( like 90% of the time ). For the rest of the 10% of the time when you do need to query the DOM, you only need to go one step below a directives element (direct children). If you are doing anything more complex, then you are doing something wrong again. For this basic case, Angular already provides you JQLite which is capable of doing the querying for simple scenarios like this.
About the helper functions that JQuery provides - angular too provides quite a few helpers - and they are sufficient for like 95% of the cases. For other stuff you might want to consider underscore.
So, why do you need to include JQuery?
Because it's the foundation of jQuery UI, and JQUI has modules that I don't feel like writing myself from scratch (sortable nested lists).
Still, the joins between Angular and JQUI (through AngularUI) are fairly visible. It's not a smooth ride at the moment.
unless you are coding all your js widgets from scratch that's not an option , and there is 0 reason to do it. jquery plugins are easy to integrate with angularjs directives , why not use them?
i used taginput, transit, autocomplete, masonry and bootstrap widgets to code this app : http://markme.alwaysdata.net/ ,are you really suggesting people should not use jquery? non sense. Angularjs is made to be used with jquery. Angulajs doesnt provide widgets , jquery + 10000 of plugins do .
And Angular isn't made to be used with jQuery (or not to be used for that matter). In fact, it's advisable to completely remove it in order to fully understand, appreciate and use Angular's internals. Otherwise you're using two huge frameworks (in terms of KB and complexity) and only use small parts of them.
Depends what you mean. Angular will use jQuery instad of it's internal jqLite if it's loaded.
Right now, we don't pull in any other libraries aside from Angular. We've found we don't need jQuery anymore, and are able to build up re-useable widgets with directives and use them across projects.
We use the optimizer as well, during our build process, so you can use require to load everything during development and then optimize everything down into a single file later. The optimizer is pretty powerful and supports several different scenarios for doing this.
I didn't see any mention of your backend for the project, but I did get the impression it was something Java (why else would you be using Maven?). I'm looking to be writing angular templates on top of a django backend (supplying a RESTful api for data access). I'd love to read more about integrating Angular with RESTful backends.
When you don't have to write any server side code at all, it can really speed things up.
If your backend exposes a textbook REST interface, integrating it with AngularJS is almost no work at all. $resource is all you need. In case you have one or two non-RESTful endpoints here and there – and honestly, who doesn't? – $http is your friend.
In an app I'm working on, I have a front-end built with Angular talking to a Tastypie back-end. Since Tastypie implements all the boring REST CRUD for you automagically, integrating the two took about fifteen minutes total.
If there are there any specific questions/issues on your mind, ask away.
Do you know of any really good tutorials emphasizing Angular's $resource service?
Not really. I just used the official documentation. The egghead.io tutorials may touch on $resource. I also found this helpful: http://www.bennadel.com/blog/2433-Using-RESTful-Controllers-...
Some Googling turned this up: https://github.com/dalcib/angular-phonecat-mongodb-rest. It may help you.
Here's something I found while Googling around: https://github.com/dalcib/angular-phonecat-mongodb-rest. It doesn't use Django and Tastypie, but you may find at least the front-end code educational.
I personally completely separate my front-end and back-end code. So while the API is built with Django, the front-end doesn't go through Django at all. It's served straight through nginx without doing anything special except maybe some simple URL rewrites for prettier URLs.
If I'm not too tied up in college work this week, I might do a write up on Tastypie and Angular integration.
Yeoman uses Grunt, which is nice for building, watching, and packaging.
Not strictly needed, but very useful and recommended.