0

I am using angular one-way-binding from controller to directive. When i update value in directive, it should not update the value in controller. But it is working as two-way binding. can some one find my error.

angular.module("myApp",[])  
  .directive("myDirective",['$timeout', function ($timeout) {
    return {
      scope: {myValue: "&myAttribute"},
      template: '<input type="text" ng-model="myValue().name">',
      link: function (scope, iElm, iAttrs) {
        var x = scope.myValue();
        console.log(x);
        $timeout(function(){
          x.name= "directive";
        },4000);
      }
    };
  }]).controller("myController", function ($scope, $timeout) {
    $scope.someObject = {name: "test"};
    $timeout(function(){
      $scope.someObject.name= "controller";
    },2000);

});

http://plnkr.co/edit/9wihx1VYgKoTK00awN67?p=preview

lch
  • 4,569
  • 13
  • 42
  • 75
  • 1
    I think the ampersand is going to create a function that returns the value of someObject in the parent scope, https://docs.angularjs.org/api/ng/service/$compile, so this is not one-way. – ryansstack Mar 23 '16 at 23:14
  • Yes, & returns a getter function. so it should be one-way – lch Mar 23 '16 at 23:15
  • 1
    It's getting a reference to an object, though, so the directive and the parent scope both have the same reference, and any changes to the object properties will naturally show up in both places – ryansstack Mar 23 '16 at 23:21
  • Digressing a bit, but if you're using Angular 1.5.x, you'll have available to you the `<` binding, which is one-way. – miqh Mar 23 '16 at 23:44

3 Answers3

2

I'm not sure if there is an easier way but you could use one-way binding with @ binding in isolated scope and add an observer on the attribute to update the object in the directive on changes.

The @ binding takes the data from the parent and pass them as string to the directive. To create an object you have to use scope.$eval(interpolatedStrValue).

(The Ampersand binding is not working as mentioned in the other answer because the getter is passing the reference to the parent object. So any change will update the parent as well.)

Please have a look at the demo below or in this fiddle.

angular.module('demoApp', [])
 .directive('oneWay', OneWay)
  .controller('mainController', MainController);
  

function OneWay($timeout, $parse) {
 return {
   scope: {
     myValue: '@myAttribute'
    },
   template: '<input type="text" ng-model="myValue.name"/>',
    link: function(scope, element, attrs) {
     console.log('link', scope.myValue);
      attrs.$observe('myAttribute', function(myValStr) {
       scope.myValue = scope.$eval(myValStr);
        console.log('controller myValue obj', scope.myValue);
      });
      
      $timeout(function() {
       console.log('change dir');
        scope.myValue.name = "directive changed";
       }, 4000);
      
      
    }
  }
}

function MainController($scope, $timeout) {
 $scope.testObj = {
   name: 'before mod.'
  };
  
  $timeout(function() {
   $scope.testObj.name = 'Controller modify';
  }, 2000);
  
  $timeout(function() {
   $scope.testObj.name = '2nd Controller modify';
  }, 8000);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.9/angular.js"></script>
<div ng-app="demoApp" ng-controller="mainController">
  Controller: {{testObj.name}}<br/>
  Directive: <one-way my-attribute="{{testObj}}"/>
</div>
AWolf
  • 8,770
  • 5
  • 33
  • 39
1

You should use "@" instead of "&" like below

scope: {myValue: "@myAttribute"}

The below link explains

What is the difference between & vs @ and = in angularJS

Community
  • 1
  • 1
Ranjith V
  • 134
  • 5
1

According to the docs (for 1.xx) https://docs.angularjs.org/api/ng/service/$compile ampersand is going to return the value of the attribute expression executed in the parent scope ( a getter ), which in this case is an object reference, and changes to any of the object properties will be reflected in both.

Changes to myValue in the directive scope won't affect the parent scope, though.

ryansstack
  • 1,396
  • 1
  • 15
  • 33