I Wish I Knew Then What I Know Now — Life With AngularJS

AngularJS is a powerful if stubborn Javascript framework. What follows is simply a list of things that I wish were clearer to me when I started working with AngularJS. Hopefully, someone just starting with AngularJS will find this list helpful.

Data Binded Directives

Manipulate the data, not the DOM. In a “normal” Javascript application, the developer is used to writing lots of event listeners to respond to users. You end up writing a lot of onChanges, for example. This gets in your way in AngularJS. AngularJS binds to your data and watches it. For example, instead of an onChange attribute on a select input, you can register a $watch() method on your controller to watch a collection. Or maybe you want to hide/show a div (using the ng-show directive) based on what value is chosen in the select.

Additionally, you can write your own custom directives to manipulate a particular DOM element and add functionality to it. Combined with services and messaging, this can be extremely powerful.

In short, when you start with AngularJS, you need to make a conscious effort to forget what you thought you knew about manipulating the DOM with Javascript.

Services are just global objects

AngularJS wires in services to your controllers for you, in an attempt at implementing dependency injection in Javascript. Many useful services are provided for you, such as $http, $scope, $location, and $routeParams. However, in any reasonably complex application  you will write your own services. In reality, services are just Javascript objects, nothing more. They are a very useful place to hold data that may need to be accessed in multiple parts of the application. Hence why I call them global (although, in reality, singletons may be the better term).

Embrace broadcast messaging

Any reasonably complex view will end up containing multiple controllers. We struggled for a while to handle communicating between those controllers. For example, take a view that has a left navigation bar and corresponding controller, as well as a main content window that also has its own controller. The behavior of the main content window depends upon selections in the left navigation bar. How do you communicate between those two controllers? When we started, in many cases broadcast messaging just felt like overkill. Once we learned to love the bomb, life got easier.

Which all leads us to…

Combine broadcast messaging with services

The pattern that we eventually fell into was to combine broadcast messaging with services. Given data that affects multiple controllers, that data is stored in a service. If a controller needs to manipulate that data, it does so via a setter exposed by the service. When this data is updated, the service broadcasts a message alerting the application that the data is changed. Any other controller that may care about that data can register a listener (a $on() method) for that message on instantiation. When it receives the message, it can get the data from the service and react accordingly.

Like many things in AngularJS, this too felt like overkill at first. But it is extremely flexible and expandable. I have never really seen this pattern described anywhere, but it seemed to come naturally to us as we grew more comfortable with AngularJS. It works very well for us.

Know when to use a ng-view

A page gets one ng-view. Assuming you have a single page application, this means you get one view. Use it wisely. Give some thought to what should be in the view. Is this your main content window or is this more of a navigation? Is the actual content (HTML) of this section highly dynamic? These are important decisions to make early in the development of your application if you have more than one distinct content area on your page.

Beware of external data manipulation

I had an application written in AngularJS with several $watch() methods watching various pieces of data. In one instance, some of the data was being manipulated by an onClick call added after the DOM was parsed by AngularJS. Since the DOM had already been parsed before the onClick was added, the $watch() method did not fire. (Note: this violated my first point in this article!). I am not sure of the best way to handle this. I ran into this issue building an application for a demonstration and just left it. I added it here as something to be aware of. If you find yourself wondering why AngularJS doesn’t always react to changes in your data, look for places that data is being manipulated that AngularJS may not be aware of.

I hope this helps out others getting started with AngularJS.

One thought on “I Wish I Knew Then What I Know Now — Life With AngularJS

  1. chad says:

    broadcast messaging with services.
    Are there any examples of this somewhere online?

  2. Jon DeJong says:

    Chad,

    I do not have any examples online. But I can put together a demo and post it to GitHub. I’ll write up another blog post detailing what I’m talking about once that is done.

  3. Jon DeJong says:

    Chad,

    I have posted an example to GitHub. There will be another blog post walking through the example up here shortly. In the meantime, the code is here:

    https://github.com/jondejong/angular-contoller-messaging

  4. Jon DeJong says:

    Sorry… that above link is wrong. The GitHub repo is here:

    https://github.com/jondejong/angular-controller-messaging

    1. Peter says:

      Great article and even better example. I was wondering if you or anyone knows a good example on how to use Google feed api and angularjs..

      I’ve been struggling with this for 3 months now and my rss/podcast app has stalled.. your service/messaging sample is brilliant because it is simple and shows off this one idea.

      I know I’m missing something simple and will laugh after i get past this hurdle.

      Thanks again, you rule!

  5. Jair Trejo says:

    Hello! Excellent article.

    I am in the middle of building a rather large Angular app, and have given a lot of thought to communicating “control” directives with “display” directives, like a date selector and a range input that together control a pie chart.

    I don’t use broadcasting at all; I just use regular AngularJS data-binding. The date and range inputs are tied (through ngModel) to scope variables, and the chart directive watches on changes to them. It works fairly well.

    So I am curious, what are the advantages to using explicit message broadcasting? I’m thinking perhaps performance, since you can control exactly when does the chart directive updates itself. On the other hand, I don’t have performance problems, even with lot’s of controls and displays.

    1. Jon DeJong says:

      Thanks for the feedback, Jair.

      It sounds like you have a different use case. I use broadcast messaging when I have sections of my page under different scopes (ie. managed by different controllers). In this example, if your chart is under one scope, and your date range is under another, then it is more challenging for the chart’s controller to watch the range. It’s not impossible to access another controller’s scope, but it’s not very clean either.

      In the case where you have multiple scopes on the page, I find it better to stash the shared data into a service like I described. The service can then broadcast when the data is updated so other controllers can react. This can be especially useful if your page is highly dynamic, and a particular controller may not even be aware of what other controllers are on the page at any given time.

      In your case, however, it sounds like your range selector and your chart are under the same scope, so you have no need to message between them, IMO.

  6. Niall says:

    Thanks for the article. For anyone looking to learn more about angular, including the ‘Broadcasting services’ bit, you really ought to check out one of the best Angular resources I’ve ever come across.
    http://egghead.io

    You’re welcome!

  7. andrew luetgers says:

    Another approach to this problem is to set up a watch on the state values in the service that change. This could be on a new scope that was created for this service or you can setup a watch on any object by using $rootScope.$watch(function() {return myService.state}, someChangeHandler);
    The benefit of this approach over a global event dispatch is that event names are essentially an informal protocol using a global namespace. Is there a central registry of these names, where they come from and who uses them? NO. Ultimately you had better name them well and document clearly what events come from where and who uses them. Using the watch approach you must inject the service and explicitly setup a watch on some property there upon. IMHO this is much more formalized and self documenting. This distinction is essentially the big win in my mind between DI and dirty checking vs event based systems like backbone and even Angular’s own broadcast and emit.

  8. Ian says:

    Thanks for posting this!

    I had kind of come up with the same solution regarding the services and broadcasting, but wasn’t sure if it was a hack or good idea. I created a local cache JSON object in the services. When views load, they can trigger the polling of it, which fires of an HTTP request, but they always get the benefit of the cached object.

    The broadcasting could be good as well if you have an app with multiple machines and records being updated. The services could either poll or use sockets. When you use that approach, you never have to trigger the requests explicitly from the controllers.

  9. Dennis says:

    I’ve recently started on utilizing angularjs, where I googled “Things i wish I knew angularjs”, and this one came up.

    Great resource, thank you for posting this.

  10. Mattias Wallenberg says:

    Perhaps you’ve figured out the solution for your last point (Beware of external data manipulation) but I’ve found it’s quite simple: Never use onClick _except_ in a directive’s linking function. In the directive, make sure you wrap whatever the click function does in scope.$apply(). Of even better, just use ng-click (inside or outside of a directive) which does exactly this for you.

  11. Thank you for a very useful article. I’m finding it to be one of those things you work with when you just stop to think from time to time Wow, how beautiful is this to work with! The messaging $emit and $broadcast particularly helped me out where instead I was setting up service objects to communicate between controllers.

    Thanks again.

  12. Andrew Gscheidle says:

    Perhaps you’ve learned this already, but to solve the problem you mention in “Beware of external data manipulation”, just process the results of external/non-DOM-triggered data manipution inside a $timeout() block. This seems to be the easiest and least error-prone way to make changes and trigger a digest cycle, which will then apply any necessary changes to the DOM. This allows you to make Angular concerned about processes not already under it purview. Works well, especially when making async calls to services, or changes using jQuery that for some reason cannot be handled using directives.

  13. Russell says:

    Great article, thanks for posting. I’ve discovered most of these things the hard way as well!

  14. Jeff says:

    Hi,

    Thanks for the article 🙂 I’m wondering why you use broadcast messaging to notify updates to a controller, instead of tying the service directly into the scope. Won’t you get watches and synced updates across controllers for free?

  15. Hugo says:

    Thank you a thousand times for the broadcast messaging and services part – and the related article.
    Helped a lot

  16. Jeremy Stover says:

    For your last point, I ran into a similar problem where I needed to add a watch on an externally loaded element. (Google map, video container, etc) and of course, I would apply the $watch() and it would fail because even if the application is loaded, the external resource of the map hasn’t necessarily loaded. TLDR; I had a function that would check in the dom tree and apply the watch() on the element, if it failed it would wait for 20ms with $timeout() and then recurse itself. This is a decent solution because it makes no assumption of Internet speed, or whether the resource is already cached. Hope that helps a bit!

  17. Ben Wheeler says:

    OP and Jeremy Stover, per the last point (angular html/code added after initial load not firing), i think the solution is to $compile the new code before you add it to the DOM.

    Example from my code:

    var button_container = $(“#buttons”);
    var newEl = $compile(“”)($scope);
    button_container.append(newEl);

    without $compile’ing, angular wasn’t recognizing that this was one of my custom directives, and it wasn’t displaying at all. with $compile, everything works and the click function is called.

  18. Dave says:

    Broadcasting on rootScope is probably one of the worst anti-patterns you can come up with in AngularJS. Not only does this create serious problems when trying to follow code execution (as everything is just thrown up in the air), you also impact hard on the digest cycle as your application and scope hierarchy grows.

    There are far better implementations of publish/subscribe where you can use a service that only notifies interested parties instead of broadcasting everything globally without any deeper thought or design.

  19. Ali Adravi says:

    Combine broadcast messaging with services: It is not good option to exchange the data between two controllers, It’s better to use the service data and other controller will automatically updated.

    thanks
    http://www.advancesharp.com

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*