简体   繁体   中英

Angular $watch nuance and factory not reflecting state change?

I have difficulty grasping why the factory will not reflect changes to its primitives (as a singleton) but it will to objects.. sometimes.. in the following six scenarios.

I have this factory:

angular.module('my.module).factory('myfactory', FactoryFunction);
function FactoryFunction(){

  var primitiveVariable = false;
  function togglePrimitive(){
    primitiveVariable = !primitiveVariable
  }
  var dummyObject = {
    isTrue = false;
  }
  function toggleObject(){
    dummyObject.isTrue = !dummyObject.isTrue;
  }
  var myFactory = {
    toggleObject: toggleObject,
    dummyObject: dummyObject,
    togglePrimitive: togglePrimitive,
    primitiveVariable: primitiveVariable
   }
   return myFactory
}

and I also have this directive

angular.module('my.module).directive('myDirective', DirectiveFunction);
DirectiveFunction.$inject['myFactory'];
function DirectiveFunction(){
  return {
    restrict: 'A',
    scope:{},
    link: function(scope, element, attributes, nullcontroller, nulltranslcude){

      //watch1 WONT RUN 
      //watched is undefined even if myFactory.togglePrimitive() runs
      scope.$watch(myFactory.primitiveVariable, function(){
        //do stuff
      });

      //watch2 WONT RUN
      //watched is undefined even if myFactory.togglePrimitive() runs
      scope.$watch(function(){return myFactory.primitiveVariable}, function(){
        //do stuff
      });

      //watch3 WONT RUN
      //not returning something by calling togglePrimitive()?
      scope.$watch(myFactory.togglePrimitive(), function(){
        //do stuff
      });

      //watch4 WILL RUN
      //but now I am not even running the function...?
      scope.$watch(myFactory.togglePrimitive, function(){
        //do stuff
      });

      //watch5 WONT RUN
      scope.$watch(myFactory.dummyObject.isTrue, function(){
        //do stuff
      });

      //watch6 WILL RUN 
      //... seriously??
      //is myObj.val and function(){return myObj.val} NOT the same?
      scope.$watch(function(){return myFactory.dummyObject.isTrue}, function(){
        //do stuff
      });
    }
  }
}

My question is, why do watch1, watch2, watch3, watch5, NOT work while watch4, wantch6 DO work?

I am interested in the why this happens. Is this a design choice? what is the purpose?

Also I would like to add that function(){ return myFactory.primitiveValue } does not work as a watch expression and yet function(){ return myFactory.dummyObject.isTrue } works as a watch expression. Does anyone know why this bias occurs between primitive vs object's primitive attribute? They are both primitives. When console.log from the factory function that updates the primitive, it reflects the change. When console.log from the expression that returns the primitive (in the $watch), it does not reflect the change.

jsFiddle: https://jsfiddle.net/b0svnjqf/21/

Any light anyone can shine into this I would greatly appreciate it.

That is because watch expression takes either a string representing the property or an expression that can be evaluated on that scope or a function getter that returns a value which is used for dirty checking every digest cycle.

So for example:

  scope.$watch(myFactory.primitiveVariable, function(){
    //do stuff
  });

watch expression is the value of myFactory.primitiveVariable and not the property itself, where as:

  scope.$watch(function(){return myFactory.primitiveVariable}, function(){
    //do stuff
  });

runs the expression getter passed in as function during every digest cycle for dirty check which will return the value of the property at that time.

Similarly when you do :

  scope.$watch(myFactory.togglePrimitive, function(){
    //do stuff
  });

you are passing the reference to the function togglePrimitive as the watch expression which can be a getter but you are really not returning anything from there so your watch listener will not run after 1 iteration.

Documentation :

watchExpression: function() | string

Expression that is evaluated on each $digest cycle. A change in the return value triggers a call to the listener.

string: Evaluated as expression

function(scope): called with current scope as a parameter.

Apart from this there is no point changing the variable primitiveVariable and expect the change to be reflected magically in myFactory.primitiveVariable they both are different and as they are primitive they don't hold on to the references as well.

function FactoryFunction(){

  var myFactory ,
      dummyObject = {
         isTrue = false;
      };

  function togglePrimitive(){
    //Set the value directly on the factory instance
    myFactory.primitiveVariable = !myFactory.primitiveVariable
    //or even this.primitiveVariable  = !this.primitiveVariable;
    //But better not to assume "this" as much as you can
  }

  function toggleObject(){
     //This is fine as you are modifying property on the reference and the same reference has been set to the dummyObject property of myFactory
     dummyObject.isTrue = !dummyObject.isTrue;
     //or this.dummyObject.isTrue = !this.dummyObject.isTrue;
  }

  myFactory = {
     toggleObject: toggleObject,
     dummyObject: dummyObject, //this should be fine unless you overwrite the variable dummyObject itself
     togglePrimitive: togglePrimitive,
     primitiveVariable: false //Set the value directly
   }  

   return myFactory;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM