angularjs anti-patterns

Anti-patterns are patterns you should not be implementing.
There are already a number of guidelines such as use directives only for dom manipulation, use of modules etc.
Here's my running list for angularjs of ones I also feel are important.
I'm no saint when it comes to these, I've probably implemented all of them at one stage or another.

Controllers doing too much (thin Controllers fat Services)
In angularjs, its easy to make behavior available from Controllers by adding functions to scope. Need to delete someone from a list? easy add a method on scope and call the delete.
Plus models in angularjs are often portrayed as simple plain old javascript objects. Again easy to put the model on scope as use as an ng-model on the page.
Many examples and tutorials show this; partly as a shortcut just to illustrate something else but none the less people see it enough times and it can become in-grained

Here's the problem with that. Your controllers can quickly contain too much logic which makes it difficult to maintain, test and reuse.
This reminds me of the Rails saying "thin controllers and fat models"

Angular controllers should be lightweight, a passthrough to some Service layer object which does "the work". You should be able to look at a controller and quickly tell what it does. If its more then a few pages then its probably doing too much.

Yes, you can reuse controllers and include in multiple views, but that level of reuse is basically at the html/partial level. I believe a separate Service object is more reusable across different views.

After writing this I decided to google "angularjs anti-patterns" and this is a good post I found which talks about the same thing with some great examples.


Not updating url and not using url to drive behavior
The url is still important in single page apps (SPAs). Most SPAs do update the hashtag when a user navigates between pages in the app (angular ui-router helps you do this).
However once on a page within a SPA there may be actions which should continue to update the url, but in many apps do not.
For example in our app we have a seller budget page which shows details for a sellers budgets. A seller could have multiple budgets so once on the page the user can switch (e.g. tab/dropdown) to a different budget for a seller e.g.
  #/seller/1/budget/2
  #/seller/1/budget/44

When the user switches budgets the url should be updated. In addition this url should be shareable and book markable and when clicked later, should open the SPA app up into that exact previous state.
If you don't update the url for in page navigation changes then the app won't behave as users expect when they share links etc. Our requirements lead told us bookmarks are important and they would like to bookmark as much as possible (the more information the better e.g. even search terms).
(note: we can't predict how many budges a user may have so can't define as a ui-router state)

What we have also found is that its best to typically drive app behavior through the url. What I mean by this is when on a page if the user does an action which should update the url then whats most effective is have that action just update the url and then have a listener which listens for url changes and then calls a function to get the app into the correct state (e.g. load data, enable/disable parts of ui).
If you follow this approach then you will
- keep the page simple as you add more controls i.e. each control change (such as select different budget) just updates the url only, nothing else. Other code elsewhere handles loading data etc.
- centralize handling of responding to nav changes
- find it easier to add deep link support
- make your users happier because deep link work as they should
We found adding behavior to every control click on the page quickly makes the controller very complicated and hard to understand (more so for complex pages with lots of controls)

trello.com provides a good example of updating the url; when a user opens a task they update the url with the task id; so if I copy the url and share it if someone opens that url they open the same task


Using $scope to share between Controllers
Ooooh its very easy to put things into $scope and use that as a sharing mechanism between controllers. You can make the case that since scope is inherited then its reasonable to use it as a means to share to child controllers lower in scope.
But doing so makes the code brittle, less reusable and harder to understand.
Just don't do it. Instead used a model which is shared.
You can implement that model as an angualrjs .service() (singleton) which can be injected into controllers and controllers have access to. Data which needs to be shared is put into the shared service.

What if I need to load data first and then put into shared model and consumers can't use until the shared data is loaded?
- I added a promise property into the shared model which consumers wait on and is resolved by the provider once the shared data is loaded and put into the model.

Controllers can also bind to properties in the Shared model so as they are updated watchers are fired.

I also read about sharing using the $cache. I have not tried but think that too is preferable to sharing via $scope.



Not using Angulars .constant()
You can use angulars .constant() on an app or module to define constants (primitives or objects) which can then be injected into Controllers etc.
Rather than sprinkling such constants as "magic numbers" around your code, .constant() is a good place to put such definitions. Easy to see at a glance and change in 1 place.


Writing unit tests that are Functional tests 
Angular has awesome support for writing unit tests (karma) AND functional tests (protractor). Be careful not to mix the two, especially writing unit tests which are really functional tests.
Unit tests should be limited to the unit of work. As a rule of thumb if your unit tests are testing across multiple pages of behavior in one test then I think its a functional test not a unit test.

This ties in with the anti pattern of doing too much in controllers. If your controllers are light and work is delegated to other objects then that makes the whole more testable.




Use of .service vs .factory vs .provider
This is a good post which outlines good guidelines as to when to use which. The general rule is
.service for singleton
.factory for new
.provider for new with configuration
I think these are good rules of thumb to follow.




Separate Service objects repeating same behavior



Comments

Popular posts from this blog

deep dive into Material UI TextField built by mui

angular js protractor e2e cheatsheet

react-router v6.4+ loaders, actions, forms and more