3

With AngularJS, how should I be triggering that the model is now dirty and needs to be saved to the server? Besides the user clicking a "save" button?

I thought of using $watch, but the thing fires when the data is first loaded so you end up with this hack all over the place:

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

You can also $emit events to trigger a save, but when you:

  1. set a property on the scope
  2. $emit an event on that scope
  3. catch the event with $on in a parent controller and look at the property

in that order, the event handler declared in step 3 will see the scope property as it was before the change was made in step 1.

I've put together an example here:

http://plnkr.co/edit/Il9TzUdCSBdewCxIKARZ

where if you click foo, then bar, then foo, then bar, you'll see that the event listener is always one step behind the property.

I'm assuming this has something to do with when scope events are handled in relation to digest cycles, but it does seem to violate the laws of javascript. How did the Angular guys manage to make it so a property doesn't change when you change it!?

This caused me some head scratching when I was always saving old data.

The quick fix is to use a $timeout to "defer" the save, I assume until the digest cycle is over. While this works, it seems messy. What's going on here? Events seem useless if changes to the scope aren't visible to the event handler, doesn't it?

Is there a clean way of doing this?

Thanks.

Community
  • 1
  • 1
nicholas
  • 14,184
  • 22
  • 82
  • 138

1 Answers1

1

This is because two way binding doesn't update until the $digest loop, which happens after your $emit.

I suggest using a $watch on the data to trigger the save.

// Controller
$scope.$watch('message', function(newVal, oldVal) {
  if (newVal === oldVal) {
    return;
  }
  // Handle update...
});

Another option is to pass an object into the directive instead of a string. By doing this, the $on listener will have access to the correct data.

For example:

// Controller
$scope.messageData = {message: "Initial message"};

// HTML
<div set-message="messageData">...</div>

// Directive
scope: {
  messageData: "=setMessage"
},
link: function(scope) {
  scope.setMessage = function(message) {
    scope.messageData.message = message;
    scope.$emit('messagechange');
  };
}
rtcherry
  • 4,840
  • 22
  • 27