AngularJs has seen a tremendous improvement over the last couple of years. With a strong core team, thousands of supporting modules in the ecosystem and backed by Google, AngularJs is the most favourite UI javascript library currently!
But ‘with great powers comes great responsibilities!‘ More and more developers are flocking to AngularJs everyday and it becomes more important to understand the nuances and code accordingly. Here are some top mistakes developers make while coding in AngularJs and the steps to avoid them!
Modularity
Oh please! Please put stuff where they belong! We all have our tendencies to piece together a quick app for a client demo or something and put all stuff in the controllers. You wont believe me how many times I have seen a controller go beyond 500 lines of code!
Use Services/Factories wherever required
Directives are meant for DOM related stuff. Use them!
Keep a watch on the size of the file. Its sometimes as simple as that. If you think it is getting big, time to refactor!
Overloading the RootScope
Lot of us think that rootScope in the angular world is some kind of a goods train and we could load all the stuff we want to on it and it would be delivered at the right spot! (Forgive my analogy!) Hope you get the point. When we want to share information between controllers or other modules, the first thing that comes to the mind is ‘Why not use rootscope!?‘
Well, at first, its all going to be fine
Then one day, rootScope is going to be a huge datastore on your client side application
This could overload angular’s digest cycle in watching these variables and cause a performance degradation
You could argue that the performance hit is going to take a while, but hey! It is going to happen!
Check out this wonderful explanation on SO about this
Too many watchesKind of relates to my previous point. Watch is usually used to ‘watch’ a variable and do operations when its value changes. The problem is that ‘watch expressions’ are run multiple (!) times during angular’s digest cycle. Boils down to a simple math. If 1 watch expression is triggered multiple times during a digest cycle, just imagine the performance hit that an app with 100 watch expressions takes!
Broadcasting (events) like a radio station!
There may be scenarios where broadcasting an event is the right way to go. But sometimes, we tend to solve the problems in the easiest way. After all, we are humans. (or are we??) . If there is a need to transfer data from modules within the app, you could use a factory, or a location parameter in the url or rootscope (sparingly!) or session/local storages or using controller inheritance (so in your child controller you would just do $scope.$parent.getSomeData())
Cancelling Timeouts/IntervalsThere might be a scenario where you might need to add a $timeout to perform some actions after a stipulated time.
Although timeouts could be totally avoided, if you really have to deal with them, cancel them after the controller is destroyed. You could listen to the controller’s destroy event like this and cancel the timeouts/intervals you created within the controller
// Cancel it when the controller is destroyed$scope.$on(‘$destroy’, function (){ $timeout.cancel($scope.timer); });
DOM manipulations
DO NOT absolutely do DOM manipulations in controller. Why?
DOM manipulations are slow
Controllers are not meant for DOM manipulations!
Directives are meant for just that.
And if you have a directive for DOM manipulation you could reuse it in any other controller. But you cant reuse it if its within the controller itself
Understanding callbacks
Understand callbacks before you jump into Angular. A lot of times timeouts have been created for the lack of understanding on callbacks. Take for example this simple scenario
You want to call a REST api and get a response
Then print something on the html. Simple.WRONG: $http.get(some_URL, function(success){}, function(error){});
$timeout(function(){
#Hey it takes 5 secs for the above call to complete. Let me just do a timeout!
$scope.message = ‘Call success’;
}, 5000);Btw, this does not exactly execute in 5 seconds. There will be a blog soon on why! (I know you know it already, but we still want to write about it!)RIGHT : 1) $http.get(some_URL, function(success){$scope.message = ‘Call success’;}, function(error){$scope.message = ‘Call failed’;});(OR)2) var promise = $http.get(some_URL);
promise.then(function(success){$scope.message = ‘Call success’;}, function(error){$scope.message = ‘Call failed’;});Read more about promises here and about callbacks here
Dependency Injection
We found this the hard way but dont inject dependencies for your angular module like thisBAD app.controller(‘MyController’, function(Dep1, Dep2){
});GOOD app.controller(‘MyController’, [‘Dep1’, ‘Dep2’, function(Dep1, Dep2){
}]);ok Why do I have this redundancy? Why are my dependencies first injected as inline array annotations and then as parameters to the controller?
Simple answer: Minification
Detailed: JS minifiers rename the arguments before minification and then Angular has no absolute idea of what those arguments are! There are two solutions to this
Either follow the implementation in the ‘Good’ section above (or)
Use tools like ng-annotate which would automatically do the inline array annotations for you.
JSHINT (static code anaylis) and Tests!
Often times, we ignore these guys as we need to meet that one deadline or go to the market before the other guy does! So much that we forget the good stuff that they bring. I am just going to jot down the advantages
JSHINT has caught more bugs than all the fish ever caught in human history (Crazy Analogy again. Forgive!)
If you had a friend who would sit with you all day while you code and test all your stuff, would you be happy?
If the answer is YES, Karma is the name! Not the literal meaning, but this awesome test runner
If the answer is NO, you need some socializing 😉(Note: if you have a large inherited codebase with no absolutely no tests, dont go about making that your priority! Of course tests are important but not more than the core business! Allocate may be 20% of your time weekly to write tests for the old code and dont write any new code without tests. Eventually you would be boasting a decent test coverage!)I am planning to do a blog later on how Panitr maintains near perfect test coverage
Following good coding practices not only enriches your knowledge but also helps the future app maintainer to not go mad and file a lawsuit against you 😉