Angular $watch VS. Event Based Processes

Angular $watch VS. Event Based Processes

Is there a pandemic with Angular $watch functions? In a current project that I am working with, there would seem to be one. So with this post, I would like to look more into Angular $watch and possible alternatives for it. What kind of drawbacks you can get with $watch and possibly some benefits. As of right now I stand with there being many issues with $watch as it has caused many bugs in our code. In my opinion using an Angular $watch is a quick way to get out of thinking about how the code should work. So with that in mind, let's see if I can find benefits that may outweigh the costs.

Event loop

The continuous loop. This can be a problem since each model update can trigger a digest to occur. In each digest, a $watch can run multiple times and is not guaranteed to run only once. This could potentially cause the $digest limit to be reached. Now in some cases using a $watch may not be avoidable. I compare this to using float: left; in CSS. Naturally a web page will try to align to the left. Calling float: left can cause many issues in IE. *Cough* Clearfix.

In the world of the web, which is naturally stateless, with a $watch you are trying to monitor state of an object. Let me explain via an example:

Let's assume that I have an object that linked to an input field.

  // Some controller/directive
  scope.myInputModelText = "";

Now I want the DOM to update when the user types something in. There are three options to go about doing this. Using a watch, using the two-way data binding that angular gives, or triggering a callback. The watches that I have seen implemented have been used in this kind of scenario. I have an input field and on changes I want to take the new value and pass it along to some functions that need to evaluate the new value change. So you are constantly watching the object to see if that value has changed.

Have you changed yet? No.
Have you changed yet? No.
Have you changed yet? No.
Have you changed yet? Yes.
Have you changed yet? Maybe?

Example

  module.directive('temp', function(){
    return {
      scope: {
        text: '='
      },
      link: function(scope, element){
        scope.$watch('text', function(newVal){
          scope.textChanged(newVal);
        });
      }
    }
  });

However if you used a callback or event-based pattern it would only know if something has changed if something told it had changed. I personally like this approach for a couple reasons.

  1. It doesn't require the client/server to use extra CPU cycles to check to see if something has changed.
  2. It's simpler to debug.
  3. It's easier to understand because it is more assertive on what it is doing.
Variable changed. Sincerely, Event Handler.

Example

  <temp changed="someParentMethod" text-model="someParentModel"/>
  module.directive('temp', function(){
    return {
      template: 
        '<div>' +
          '<input type="text" 
                  ng-change="textChanged()" 
                  ng-model="textModel"/>' +
        '</div>',
      scope: {
        textModel: '=',
        changed: '='
      },
      link: function(scope, element){
        scope.textChanged = function(){
          scope.changed(scope.textModel);
        }
      }
    }
  });

The nice thing about this is that angular uses the event pattern for ng-change. So this means it isn't constantly looping to see if something changed. "Now Rizo, ng-change only works on inputs." Ah, you are right, but this is where we take advantage of other functionalities that Angular or plain old JavaScript has.

Two-way data bindings

Like with anything in programming, it is a good idea to know and explore other options that may be available to you. Saying that two-way data bindings are the way to go 100% of the time would cause a lot of pain. At least in angular, this is used a ton but it also has its own drawbacks. If data models are changing all the time, this will trigger the digest cycle to occur. If the models are large or if you have many of them, it can cause the $digest limit 10... error to show up. Some nicer features in Angular 1.3 is one way data bindings {{:myVar}} which set a value to the DOM once it receives a value. It then removes itself from the watch list so it doesn't update anymore. A caveat is the one-way bindings will accept an empty string as a valid input. So you have to look out for that.

Is there any reason to use a $watch?

So where can a $watch be useful? For me, it is tough to say. Mainly because I feel like I can get around not using a $watch. I never needed them for normal JavaScript and I still don't see much of a benefit knowing that you can do a lot of what a $watch offers with events or even by using the default two-way data binding that angular offers.

I was trying to figure out a good example for using a $watch. The first thing I thought might be a good idea would be a stock ticker. But then again, you can have a model that is bound to a property that gets reassigned and the body will update. Inputs and HTML elements can have events triggered either with angular or jQuery to get notified of a change. If you want to check to see if a property changed on an unrelated object, many may say that is a good scenario, but you can overwrite getters and setters to throw an event when it changes so you don't have to constantly poll the object. Or if overwriting the getters and setters is a bad practice, create your own.

Final Thoughts

There is a plethora of blogs out there showing how you can mitigate a lot of pitfalls that angular comes with. My company, in particular, has wanted to move away from it for many reasons, which can be discussed in another blog post.

Now I am still learning every day about angular but I feel like these are some of the things that should be considered. They shouldn't be willy-nilly thrown in just because it is the easiest solution. Where I work, we are seeing the issues with using $watch because it is so easy to use. I feel like in very off cases they should be used, if at all.