Jamesla
Jamesla

Reputation: 1408

How can I watch a directive variables from a parent controller?

I have a controller called main which has two nested directives called formDirective and dataDirective.

I have the following form in my formDirective view.

<form name="menuForm">
    <h4>What is your name</h4>
    <input name="name" ng-value="" ng-model="name">
</form>

I am trying to use $scope.watch to update some data in dataDirective when a form value is changed in formDirective.

When I put the following code inside my formDirective controller it works fine.

$scope.$watch('[menuForm.name.$viewValue]', function () {
  console.log('name changed');
  //update data directive here
}, true);

However this won't work for my solution because I need this code inside my parent controller as the directives need to communicate with each other and doing it this way would cause encapsulation issues.

When I put the following watcher inside my main controller it doesn't work - where I would expect it too. (I can browse it fine from web inspector).

$scope.$watch('[$scope.menuForm.name.$viewValue]', function () {
      console.log('name changed');
      //update data directive here
    }, true);

Is anybody able to tell me how I can watch my formDirectives variable from my main controller?

Upvotes: 0

Views: 1782

Answers (2)

rnrneverdies
rnrneverdies

Reputation: 15647

Is anybody able to tell me how I can watch my formDirectives variable from my main controller?

You can create directive with isolated scope and set up bi-directional binding between a local scope property and the parent scope property.

Example:

var app = angular.module("app",[])

.controller("mainctrl", function($scope) {
   $scope.name = "";
})

.directive("myFormDirective", function() {
    return {      
      restrict: 'E',    
      scope: {
        model: '=myModel'
      },
      template: '<form name="menuForm"><h4>What is your name (Form Directive)</h4>' + 
        '<input name="name" ng-model="model"></form><p>',
      link: function(scope, element, attrs) {}
    };
})

.directive("myDataDirective", function() {
    return {      
      restrict: 'E',    
      scope: {
        model: '=myModel'
      },
      template: '<p>Data Directive: {{model}}</p>',
      link: function(scope, element, attrs) {}
    };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="mainctrl">
    <my-form-directive my-model="name"></my-form-directive>
    <my-data-directive my-model="name"></my-data-directive>
    <p ng-show="name">Controller $scope.name={{name}}</p>
</div>

Upvotes: 0

user3225349
user3225349

Reputation: 69

Adding a watch inside the MainCtrl should work when $scope is removed.

Change: $scope.$watch('[$scope.menuForm.name.$viewValue]' to $scope.$watch('[menuForm.name.$viewValue]'


Another option would be to pass a callback into your directive.

Example Fiddle: http://jsfiddle.net/rfkjLst3/13/

Html

<div class="m" ng-controller="MainCtrl">
    <div>Main Ctrl</div>
    <h3>(Shared Scope)</h3>
    <directive1></directive1>
    <directive2></directive2>
    <hr />
    <h3>(Isolate Scope)</h3>
    <directive3 on-update="onUpdate(newVal, oldVal, $scope)"></directive3>
    <directive4 some-var="updateVar"></directive4>
</div>

Main Ctrl

function MainCtrl($scope) {
    // Shared Scope
    $scope.name = '';
    $scope.latestValue = '';
    $scope.onChangeFn = watchFn; // Callback for shared scope directive

    // Watching from Main Ctrl
    $scope.$watch('[menuForm.name.$viewValue]', MainCtrlWatch, true);

    // Isolate Scope
    $scope.updateVar = '';
    $scope.onUpdate = function updateFn(newValue, oldValue) {
        $scope.updateVar = newValue || '';
        console.log('hi',newValue, oldValue, $scope);
    }
}

Shared Scope Directives

app.directive('directive1', function ($window) {
    var directive = {
        link: link,
        restrict: 'EA',
        template: '<div class="d"><b>Directive 1</b>' +
            '<form name="menuForm">' +
            '<label for="name">What is your name: </label>' +
            '<input name="name" ng-value="" ng-model="name" required>' +
            '</form></div>'
    };
    return directive;

    function link(scope, element, attrs) {
        // Watch from inside Directive working using shared scope
        scope.$watch('[menuForm.name.$viewValue]', scope.onChangeFn, true);
    }
});

app.directive('directive2', function ($window) {
    //Notice no scope: { ... } so the scope is the parents.
    var directive = {
        link: link,
        restrict: 'EA',
        template: '<div class="d d2"><b>Directive2</b> <br /> {{ latestvalue }}</div>'
    };
    return directive;
    function link(scope, element, attrs) {}
});

Isolate Scope Directives

app.directive('directive3', function ($window) {
    var directive = {
        link: link,
        restrict: 'EA',
        scope: {
            onUpdate: '&'
        },
        template: '<div class="d"><b>Directive 3</b>' +
            '<form name="menuForm">' +
            '<label for="name">What is your name: </label>' +
            '<input name="name" ng-value="" ng-model="name" required>' +
            '</form></div>'
    };
    return directive;

    function link(scope, element, attrs) {
        scope.name = '';

        // Watching from inside the directive and firing callback
        scope.$watch('[menuForm.name.$viewValue]', function (newVal,oldVal,$scope) {
            $scope.onUpdate({newVal: newVal, oldVal: oldVal});
        }, true);
    }
});

app.directive('directive4', function ($window) {
    var directive = {
        link: link,
        restrict: 'EA',
        scope: {
            someVar: '='
        },
        template: '<div class="d d2"><b>Directive4</b> <br /> {{ someVar }}</div>'
    };
    return directive;
    function link(scope, element, attrs) {}
});

Upvotes: 3

Related Questions