1

I was creating a one way binded directive which had an attribute containing an array as follows:

app.directive('interestBox', function() {
    return {
        restrict: 'E',
        scope: {
            interests: '@'
        },
        templateUrl: '/static/templates/directive_templates/interestbox.html',
        link: function(scope, element, attrs) {
            scope.interests = scope.$eval(scope.interests); //currently, doing this for converting string to array
            console.log(scope.interests);
        }
    }
})

Here's the markup:

<interest-box interests="{{profile_data.interests}}">
</interest-box>

Controller:

$scope.profile_data.interests = [1, 2, 3, 4];

First off, the reason I am using @ and not = is that I don't need a two way binding between my controller and directive(am I correct regarding this assumption?)

But as you have it, @ parses the DOM value at the time into a string. Now, since my attribute 'interests' is an array, I need to convert it into an array from a string(which @ converts it into) in the directive. For that I am doing:

$scope.interests = scope.$eval(scope.interests);

And this doesn't feel right to me. What would be the best way to get my array from the string?

2
  • Do you want that changes in interests array from directive not to reflect in outer original data (so you need a clone) or you just don't want a watcher for two-way binding? Commented Oct 25, 2015 at 9:44
  • Changes in interests array should not reflect in outer data immediately. Thats why I kept an isolate scope. However, when a button is clicked indicating that interests have been frozen then I need to communicate to the controller the new value of interests array. Commented Oct 25, 2015 at 9:47

2 Answers 2

1

You want to have a copy of the original array in your directive to manipulate with. After modified data is ready you can update original controller array with isolated local data. In your case it will look like this:

angular.module('demo', [])
.controller('DemoController', function($scope, $timeout) {
    $scope.profile_data = {interests: [1,2,3]};
    $timeout(function() {$scope.profile_data.interests.push(4);}, 1000);
})

.directive('interestBox', function($timeout, $parse) {
    return {
        restrict: 'E',
        scope: {
            interestsOrig: '=interests'
        },
        template: '<pre>{{ interests }}</pre><button ng-click="updateInterests()">Update controller array</button>',
        link: function(scope, element, attrs) {
            
            scope.interests = angular.copy(scope.interestsOrig);
            
            // Modify interests, push new, remove old
            $timeout(function() {
                scope.interests.push(5); 
                scope.interests.splice(0, 1)
            }, 2000);
            
            // Update original controller interests with local
            scope.updateInterests = function() {
                scope.interestsOrig = scope.interests;
            };
        }
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>

<div ng-app="demo" ng-controller="DemoController">

    <h4>Controller</h4>
    <pre>{{ profile_data.interests }}</pre>

    <h4>Directive</h4>
    <interest-box interests="profile_data.interests"></interest-box>

</div>

Sign up to request clarification or add additional context in comments.

2 Comments

one other thing, should i be using a service in the directive to get the interests array?
You could, it depends on your needs. But you would still need to clone it inside.
0

It's always wrong to share a variable from one type to another type and then perform a cast

According to the AngularJS Doc, when you declare as Object the scope property of the directive definition object, you will get a Directive with an Isolated Scope...

In this case, I Think, you don't need for an Isolated Scope but a Simply Child Scope. When a directive is created with a Child Scope the values of the parent scope are copied to the new child scope...

angular
  .module('test', [])
  .controller('ProfileCtrl', function($scope) {
    var vm = $scope;
  
    vm.interests = [1, 2, 3, 4, 5, 6];
  })
  .directive('interests', function() {
  
    function InterstsPostLink(iScope, iElement, iAttributes) {
      iScope.interests = [].concat(iScope.interests)
      iScope.addInterest = function(val) {
          iScope.interests.push(val)
      };
      
      iScope.newInterest = 'Hello';
    }
  
    var ddo = {
      templateUrl: 'interests-list',
      link: InterstsPostLink,
      scope: true
    };
    return ddo;
  })
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<article data-ng-app="test">
  <div data-ng-controller="ProfileCtrl">
    
    <h3>ProfileCtrl Value</h3>
    <div ng-bind="interests | json"></div>
    <hr />
    
    
    <div interests></div>
  </div>
  
  <script type="text/ng-template" id="interests-list">
    <h3>Directive Value</h3>
    <div ng-bind="interests | json"></div>
    <input type="text" ng-model="newInterest">
    <button ng-click="addInterest(newInterest)">Add</button>
  </script>
</article>

1 Comment

Does this solution changes the value of the array in the controller as soon as it is changed in the directive(as they are sharing scopes)? Because I dont want that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.