How do I ignore the initial load when watching model changes in AngularJS?

Kevin Hoffman picture Kevin Hoffman · Jun 5, 2013 · Viewed 67.2k times · Source

I have a web page that serves as the editor for a single entity, which sits as a deep graph in the $scope.fieldcontainer property. After I get a response from my REST API (via $resource), I add a watch to 'fieldcontainer'. I am using this watch to detect if the page/entity is "dirty". Right now I'm making the save button bounce but really I want to make the save button invisible until the user dirties the model.

What I am getting is a single trigger of the watch, which I think is happening because the .fieldcontainer = ... assignment takes place immediately after I create my watch. I was thinking of just using a "dirtyCount" property to absorb the initial false alarm but that feels very hacky ... and I figured there has to be an "Angular idiomatic" way to deal with this - I'm not the only one using a watch to detect a dirty model.

Here's the code where I set my watch:

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

I just keep thinking there's got to be a cleaner way to do this than guarding my "UI dirtying" code with an "if (dirtyCount >0)"...

Answer

MW. picture MW. · Sep 20, 2013

The first time the listener is called, the old value and the new value will be identical. So just do this:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

This is actually the way the Angular docs recommend handling it:

After a watcher is registered with the scope, the listener fn is called asynchronously (via $evalAsync) to initialize the watcher. In rare cases, this is undesirable because the listener is called when the result of watchExpression didn't change. To detect this scenario within the listener fn, you can compare the newVal and oldVal. If these two values are identical (===) then the listener was called due to initialization