2

I am struggling with data binding in AngularJs.

I have the following piece of markup in .html file that includes the custom directive:

<my-directive ng-repeat="i in object" attr-1="{{i.some_variable}}"></my-directive>

Note: 'some-variable' is being updated every 10 seconds(based on the associate collection and passed to template through controller).

The directive's code includes:

myApp.directive('myDirective', function () {

  scope: {
   'attr-1': '=attr1'

which throws this exception because of the brackets in attr-1(see html code above).

It works though if I use read-only access(note at sign below):

myApp.directive('myDirective', function () {

  scope: {
   'attr-1': '@attr1'

I use scope.attr-1 in directive's HTML to show its value.

The problem is that with read-only access UI is not reflecting the change in attribute change.

I've found solution with $parse or $eval(couldn't make them work tho). Is there a better one there?

Community
  • 1
  • 1
etual
  • 561
  • 1
  • 4
  • 13
  • 2
    One thing you can do is rewrite your directive to require ng-model. That should handle the updating issues you have. – Ronald91 Oct 09 '15 at 19:40
  • Thank you for reply. Would probably work but I wanted to preserve an isolated scope in the directive I created. – etual Oct 12 '15 at 18:08

2 Answers2

3

You'll need only two-way binding and I think $parse or $eval is not needed.

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

It uses $interval to simulate your updating but the update can also come from other sources e.g. web socket or ajax request.

I'm using controllerAs and bindToController syntax (AngularJs version 1.4 or newer required) but the same is also possible with just an isolated scope. See guide in angular docs.

The $watch in the controller of the directive is only to show how the directive can detect that the data have changed.

angular.module('demoApp', [])
 .controller('MainController', MainController)
 .directive('myDirective', myDirective);

function MainController($interval) {
 var self = this,
        refreshTime = 1000; //interval time in ms
    
    activate();
    
    function activate() {
     this.data = 0;
     $interval(updateView, refreshTime);
    }
    
    function updateView() {
     self.data = Math.round(Math.random()*100, 0);
    }
}

function myDirective() {
 return {
     restrict: 'E',
        scope: {
        },
        bindToController: {
            data: '='
        },
        template: '<div><p>directive data: {{directiveCtrl.data}}</p></div>',
        controller: function($scope) {
            $scope.$watch('directiveCtrl.data', function(newValue) {
             console.log('data changed', newValue);
            });
        },
        controllerAs: 'directiveCtrl'
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-app="demoApp" ng-controller="MainController as ctrl">
    model value in ctrl. {{ctrl.data}}
    <my-directive data="ctrl.data"></my-directive>
</div>
AWolf
  • 8,770
  • 5
  • 33
  • 39
  • The answer is very descriptive but I believe this solved a different problem. I updated the question with some details to make it more accurate. For example, ng-repeat is the important detail here since it's leading to the problem. Also, I use templateUrl because it's many lines of html code that I don't want to store in directive.js due to maintainability reasons. – etual Oct 13 '15 at 14:26
  • I've created another jsfiddle with `ng-repeat`. Please have a look [here](http://jsfiddle.net/awolf2904/eL8f0krr/). I think it's doing what you're looking for. – AWolf Oct 14 '15 at 18:47
0

I've come to the following solution(in case somebody runs into the the same problem):

// Directive's code

myApp.directive('myDir', function () { return {
    restrict: 'E',

    templateUrl: function () {
        return 'my-dir.html';
    },
    scope: {
        'id': '@arId',
        'x': '@arX',
        'y': '@arY',
        //....
    },
    link: function ($scope, element, attrs) {

         // *** SOLUTION ***
         attrs.$observe('arId', function (id) {
             $scope.id = id;
         });
         //...
    }

Update: somebody sent me this answer, they have the same problem and came up with a very similar if not exact same solution:

Using a directive inside an ng-repeat, and a mysterious power of scope '@'

It is useful to read because they explain what's the idea behind it.

Community
  • 1
  • 1
etual
  • 561
  • 1
  • 4
  • 13