arete
arete

Reputation: 1933

Isolated scope directive with two way binding doesn't reflect changes in controller's scope

Summary :

When attempting to modify a scope variable (an array) that has been set for two way data binding within an isolate scoped directive, the modifications to the variable within the isolated scope directive do not reflect the changes within the controller's scope.

Toy Example :

Currently I have a sorting directive that I want to define on a table that is using ng-repeat. ng-repeat is using the same data in the ATableController controller scope to create the table:

$scope.tableData = [{'col1' : 1,'col2' : 'fff'},
                             {'col1' : 2,'col2' : 'aaa'},
                             {'col1' : 3,'col2' : 'bbb'},
                             {'col1' : 4,'col2' : 'ccc'}];

I want the sorting directive to manipulate the same data from within its isolated scope.

Set up in the directive as the following

... scope :
                  {
                    tableSortData        : '=',
                    tableSortRowAccessor : '=',
                    tableSortPrimer      : '='
                  },
...

Referred to in index as the following

...
<body ng-controller="ATableController">
       <table table-sort table-sort-data="tableData">
...

Within the link function a click handler is assigned to the table header. On click this handler fires off the sort function which eventually runs this line of code.

scope.tableSortData = []; //which would be a sorting line in the real code

These changes are NOT made from within the controller and thus the table doesn't change as expected.

Is this a vanilla js problem, (wrong references are being made, etc...) or is it something with my angular directive. Either way, what am I doing wrong and how can this be fixed?

The Code :

plunkr

http://plnkr.co/edit/5yxRj6?p=info

index.html

  <head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script data-require="angular.js@*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="ATableController">
   <table table-sort table-sort-data="tableData">
    <thead>
        <tr>
            <th>Column1</th>
            <th>Column2</th>
        </tr>

    </thead>
    <tbody>
        <tr ng-repeat="row in tableData" >
            <td>{{row.col1}}</td>
            <td>{{row.col2}}</td>
        </tr>
    </tbody>
   </table>
  </body>

</html>

script.js

var myApp = angular.module('myApp', [])

  .controller('ATableController', function($scope)
  {
      $scope.tableData = [{'col1' : 1,'col2' : 'fff'},
                         {'col1' : 2,'col2' : 'aaa'},
                         {'col1' : 3,'col2' : 'bbb'},
                         {'col1' : 4,'col2' : 'ccc'}];

  })

  .directive('tableSort', [function()
  {
    return {
      restrict : 'A',
      replace  : false,

      scope :
      {
        tableSortData        : '=',
        tableSortRowAccessor : '=',
        tableSortPrimer      : '='
      },

      link : function(scope, element)
      {
        console.log(scope.tableSortData);

        // Await click events on header
        element.find('th').on('click.header', function(event)
        {
          var field = $(event.target).attr('field-name');
          augmentTableSortData();
        });

        function augmentTableSortData()
        {
          console.log(scope);
          console.log(scope.tableSortData);
          scope.tableSortData = [];
          console.log(scope.tableSortData);
        }

      }
    }

  }])

Upvotes: 1

Views: 1908

Answers (1)

lucuma
lucuma

Reputation: 18339

The issue is that you are not calling scope.apply in your click function:

 function augmentTableSortData()
    {
      console.log(scope);
      console.log(scope.tableSortData);
      scope.tableSortData = [];
      console.log(scope.tableSortData);
      scope.$apply();   // <------ this needs to be called because the scope data is being modified in the click function outside of angular's digest cycle.
    }

Demo: http://plnkr.co/edit/as5Wek?p=preview

Documentation:

Scopes provide APIs ($apply) to propagate any model changes through the system into the view from outside of the "Angular realm" (controllers, services, Angular event handlers).

Upvotes: 4

Related Questions