flapas
flapas

Reputation: 583

Passing asynchronously obtained data to a directive

I currently have an AngularJS controller that is basically getting some JSON asynchronously through a $http.get() call, then linking the obtained data to some scope variable.

A resumed version of the controller code:

mapsControllers.controller('interactionsController', ['$http', function($http) {
  var ctrlModel = this;

  $http.get("data/interactionsPages.json").
  success(function(data) {
    ctrlModel.sidebar = {};
    ctrlModel.sidebar.pages = data;
  }).
  error(function() {...});
}]);

Then, I have a custom directive which receives those same scope variables through a HTML element.

A resumed version of the directive code:

mapsDirectives.directive('sidebar', function() {
  return {
    restrict : 'E',
    scope : {
      pages : '@'
    },
    controller : function($scope) {            
      $scope.firstPage = 0;

      $scope.lastPage = $scope.pages.length - 1;

      $scope.activePage = 0;

      //...
    },
    link : function(scope) {
      console.log(scope.pages);
    },
    templateURL : 'sidebar.html'
  }
});

A resumed version of the HTML:

<body>
  <div ng-controller='interactionsController as interactionsCtrl'>
    <mm-sidebar pages='{{interactionsCtrl.ctrlModel.sidebar.pages}}'>
    </mm-sidebar>
  </div>
</body>

The problem is, since the $http.get() is asynchronous, the directive is being badly initialised (e.g: $scope.pages.length - 1 is undefined).

I couldn't find anything that solved this problem for me, although there are some presented solutions that would seem to solve the case. Namely, I tried to watch the variables, only initialising the variables after detected changes, as suggested in many other posts. For testing, I used something like:

//... inside the directive's return{ }
link: function() {
  scope.$watch('pages', function(pages){
    if(pages)
      console.log(pages);
  });
}

I've tested it, and the $watch function wasn't called more than once (the logged value being undefined), which, I assume, means it isn't detecting the change in the variable value. However, I confirmed that the value was being changed.

So, what is the problem here?

Upvotes: 4

Views: 5777

Answers (2)

bluetoft
bluetoft

Reputation: 5443

The problem appears to be your html markup.

In your controller you have specified the ctrlModel is equal to this.

In your html markup you have declared the same this to be named interactionsController.
So tacking on ctrlModel to interactionsController is incorrect.

<body>
  <div ng-controller='interactionsController as interactionsCtrl'>
    <!-- remove this -->
    <mm-sidebar pages='{{interactionsCtrl.ctrlModel.sidebar.pages}}'>

    <!-- replace with this -->
    <mm-sidebar pages='{{interactionsCtrl.sidebar.pages}}'>

    </mm-sidebar>
  </div>
</body>

Upvotes: 1

Jasen
Jasen

Reputation: 14250

Move the declaration for the sidebar object in the controller and change the scope binding to =.

mapsDirectives.controller("interactionsController", ["$http", "$timeout",
    function($http, $timeout) {
        var ctrlModel = this;
        ctrlModel.sidebar = {
            pages: []
        };
      /*
      $http.get("data/interactionsPages.json").
          success(function(data) {
          //ctrlModel.sidebar = {};
          ctrlModel.sidebar.pages = data;
       }).
       error(function() {});
      */

      $timeout(function() {
        //ctrlModel.sidebar = {};
        ctrlModel.sidebar.pages = ["one", "two"];
      }, 2000);
    }
]);

mapsDirectives.directive('mmSidebar', [function() {
    return {
      restrict: 'E',
      scope: {
        pages: '='
      },
      controller: function() {},
      link: function(scope, element, attrs, ctrl) {
        scope.$watch("pages", function(val) {
          scope.firstPage = 0;
          scope.lastPage = scope.pages.length - 1;
          scope.activePage = 0;
        });
      },
      templateUrl: 'sidebar.html'
    };
}]);

Then match the directive name and drop the braces.

<mm-sidebar pages='interactionsCtrl.sidebar.pages'>
</mm-sidebar>

Here's a working example: http://plnkr.co/edit/VP79w4vL5xiifEWqAUGI

Upvotes: 2

Related Questions