Angularjs watch for change in parent scope

2020-02-17 06:55发布

问题:

I'm writing a directive and I need to watch the parent scope for a change. Not sure if I'm doing this the preferred way, but its not working with the following code:

  scope.$watch(scope.$parent.data.overlaytype,function() {
    console.log("Change Detected...");
  })

This it logged on window load, but never again, even when overlaytype is changed.

How can I watch overlaytype for a change?

Edit: here is the entire Directive. Not entirely sure why I'm getting a child scope

/* Center overlays vertically directive */
aw.directive('center',function($window){
  return {
    restrict : "A",
    link : function(scope,elem,attrs){

      var resize = function() {
        var winHeight = $window.innerHeight - 90,
            overlayHeight = elem[0].offsetHeight,
            diff = (winHeight - overlayHeight) / 2;
            elem.css('top',diff+"px");
      };

      var watchForChange = function() {
        return scope.$parent.data.overlaytype;
      }
      scope.$watch(watchForChange,function() {
        $window.setTimeout(function() {
          resize();
        }, 1);
      })

      angular.element($window).bind('resize',function(e){
        console.log(scope.$parent.data.overlaytype)
        resize();
      });
    }
  };
});

回答1:

You should have the data property on your child scope, scopes use prototypal inheritance between parent and child scopes.

Also, the first argument the $watch method expects is an expression or a function to evaluate and not a value from a variable., So you should send that instead.



回答2:

If you want to watch a property of a parent scope you can use $watch method from the parent scope.

//intead of $scope.$watch(...)
$scope.$parent.$watch('property', function(value){/* ... */});

EDIT 2016: The above should work just fine, but it's not really a clean design. Try to use a directive or a component instead and declare its dependencies as bindings. This should lead to better performance and cleaner design.



回答3:

I would suggest you to use the $broadcast between controller to perform this, which seems to be more the angular way of communication between parent/child controllers

The concept is simple, you watch the value in the parent controller, then, when a modification occurs, you can broadcast it and catch it in the child controller

Here's a fiddle demonstrating it : http://jsfiddle.net/DotDotDot/f733J/

The part in the parent controller looks like that :

$scope.$watch('overlaytype', function(newVal, oldVal){
    if(newVal!=oldVal)
        $scope.$broadcast('overlaychange',{"val":newVal})
});

and in the child controller :

$scope.$on('overlaychange', function(event, args){
    console.log("change detected")
    //any other action can be perfomed here
});

Good point with this solution, if you want to watch the modification in another child controller, you can just catch the same event

Have fun

Edit : I didn't see you last edit, but my solution works also for the directive, I updated the previous fiddle ( http://jsfiddle.net/DotDotDot/f733J/1/ )

I modified your directive to force it to create a child scope and create a controller :

directive('center',function($window){
  return {
    restrict : "A",
    scope:true,
    controller:function($scope){
        $scope.overlayChanged={"isChanged":"No","value":""};
        $scope.$on('overlaychange', function(event, args){
        console.log("change detected")
        //whatever you need to do

    });
  },
link : function(scope,elem,attrs){

  var resize = function() {
    var winHeight = $window.innerHeight - 90,
        overlayHeight = elem[0].offsetHeight,
        diff = (winHeight - overlayHeight) / 2;
        elem.css('top',diff+"px");
  };
  angular.element($window).bind('resize',function(e){
    console.log(scope.$parent.data.overlaytype)
    resize();
      });
    }
  };
});


回答4:

If you're looking for watching a parent scope variable inside a child scope, you can add true as second argument on your $watch. This will trigger your watch every time your object is modified

$scope.$watch("searchContext", function (ctx) {
                ...
            }, true);


回答5:

Alright that took me a while here's my two cents, I do like the event option too though:

Updated fiddle http://jsfiddle.net/enU5S/1/

The HTML

<div ng-app="myApp" ng-controller="MyCtrl">
    <input type="text" model="model.someProperty"/>
    <div awesome-sauce some-data="model.someProperty"></div>
</div>

The JS

angular.module("myApp", []).directive('awesomeSauce',function($window){
  return {
    restrict : "A",
      template: "<div>Ch-ch-ch-changes: {{count}} {{someData}}</div>",
      scope: {someData:"="},
      link : function(scope,elem,attrs){
        scope.count=0;
        scope.$watch("someData",function() {
            scope.count++;
        })
    }
  };
}).controller("MyCtrl", function($scope){
    $scope.model = {someProperty: "something here");
});

What I'm showing here is you can have a variable that has two way binding from the child and the parent but doesn't require that the child reach up to it's parent to get a property. The tendency to reach up for things can get crazy if you add a new parent above the directive.

If you type in the box it will update the model on the controller, this in turn is bound to the property on the directive so it will update in the directive. Within the directives link function it has a watch setup so anytime the scope variable changes it increments a counter.

See more on isolate scope and the differences between using = @ or & here: http://www.egghead.io/