7

Consider this Plunkr.

Inside of my watch listener, I'd like to update a property on a resolved promise.

  • Is it correct to modify values on the $$v property?
  • If not, then what should I do?

Here is the HTML:

<!DOCTYPE html>
<html id="ng-app" ng-app="myAngularApp">

  <head>
    <script data-require="angular.js@*" data-semver="1.2.0-rc2" src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script>
    <script src="script.js"></script>
  </head>

  <body ng-controller="MyController">
    <input ng-model="myDto.Weight" />{{myDto.Status}}
  </body>

</html>

Here is the JavaScript:

var myDto = {Weight: 200, Status: 'Acceptable'};

var myAngularApp = angular.module('myAngularApp', []);

myAngularApp.factory('myService', function($q){
  return {
    getMyDto: function(){
      var deferred = $q.defer();
      deferred.resolve(myDto);
      return deferred.promise;
    }
  };
});

myAngularApp.controller('MyController', function MyController($scope, myService){
  $scope.myDto = myService.getMyDto();
  $scope.$watch('myDto.Weight', function(newVal, oldVal){
    if (newVal < 150) {
      $scope.myDto.$$v.Status = 'Too Light!'; // Is this the recommended way of doing it?
    }
    else if (500 < newVal) {
      $scope.myDto.$$v.Status = 'Too Heavy!';
    }
    else if (Number(newVal)) {
      $scope.myDto.$$v.Status = 'Acceptable';
    }
    else {
      $scope.myDto.$$v.Status = 'Not a number!';
    }
  });
});
Jim G.
  • 15,141
  • 22
  • 103
  • 166
  • The other answers do a good job of addressing this specific question; however, it should be noted that any variable or property in Angular prefixed with `$$` is a private variable and shouldn't be modified or depended upon. – Michelle Tilley Sep 09 '13 at 03:51
  • @BrandonTilley: I suspected as much, but I feel like the AngularJS documentation should call this out specifically somewhere and tell people what to do instead. – Jim G. Sep 09 '13 at 04:04

2 Answers2

8

I wouldn't modify $$v directly, it's an implementation detail. Instead, use then to get the result of the promise, then use it however you like. That requires minimal change to your code.

Demo link

myAngularApp.controller('MyController', function MyController($scope, myService){
  ($scope.myDto = myService.getMyDto()).then(function(myDto) {
    $scope.$watch('myDto.Weight', function(newVal, oldVal){
      if (newVal < 150) {
        myDto.Status = 'Too Light!'; // Is this the recommended way of doing it?
      }
      else if (500 < newVal) {
        myDto.Status = 'Too Heavy!';
      }
      else if (Number(newVal)) {
        myDto.Status = 'Acceptable';
      }
      else {
        myDto.Status = 'Not a number!';
      }
    });
  });
});
Buu
  • 49,745
  • 5
  • 67
  • 85
  • Thanks Buu! This was the piece I needed to get my watch on a promise variable returned from a function to work in my code... Awesome! – James Eby Jan 22 '14 at 06:39
3

It is a little bit weird to modify the promise since a promise represents the result of an action that is performed asynchronously. It is self-contained.

I think it is better to add another service function to update data the status. Try this

myAngularApp.factory('myService', function ($q) {
    var deferred = $q.defer();
    return {
        getMyDto: function () {
            deferred.resolve(myDto);
            return deferred.promise;
        },
        updateStatus: function (status) {
            myDto.Status = status;
            deferred.resolve(myDto);
            return deferred.promise;
        }
    };
});

myAngularApp.controller('MyController', function MyController($scope, myService) {
    $scope.myDto = myService.getMyDto();
    $scope.$watch('myDto.Weight', function (newVal, oldVal) {
        if (newVal < 150) {
            myService.updateStatus('Too Light!');
        } else if (500 < newVal) {
            myService.updateStatus('Too Heavy!');
        } else if (Number(newVal)) {
            myService.updateStatus('Acceptable');
        } else {
            myService.updateStatus('Not a number!');
        }
    });
});

Updated Plunker

zs2020
  • 53,766
  • 29
  • 154
  • 219
  • Interesting. It looks like you're keeping the state in the service, and I've seen other people recommend doing the same. – Jim G. Sep 08 '13 at 03:42
  • @JimG. Technically you can handle the data anywhere. However you should make the service self-contained, and it is more testable and easy to maintain. And you'd better use a function call to update the data, not the way demonstrated in the other answer. – zs2020 Sep 08 '13 at 03:46
  • If the view is changing Weight directly, why is it bad to have the controller update Status? Creating a whole new method in the service just to modify a exposed property, it doesn't feel like JavaScript (or AngularJS) to me. – Buu Sep 08 '13 at 04:21
  • in your code myDto is a promise so where is the difference to OPs code? – niklas Dec 01 '15 at 12:01