2

I'm trying to use the jQuery fancy tree plugin with Angular. The source data for the tree comes from an ajax call in my controller. I'm then trying to get that data into my directive and load the tree. Say the service takes 2 seconds to get the data. Heres a cut down version of my code.

HTML

<div tree-view ></div>

SERVICE

angular.module('sc.services', []).
  factory('scService', function ($http) {

      var scApi = {};

      scApi.getsc = function () {
          return $http({
              url: config.Url.sc
          });
      };

      return scApi;
  });

CONTROLLER

angular.module('sc.controllers', []).
  controller('scController', function ($scope, scService) {

      scService.getsc().success(function (response) {
          $scope.sc = response;
      });
  });

DIRECTIVE

angular.module('sc.directives', [])
    .directive('treeView',function () {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                scope.$watch('sc', function() {
                    $(element).fancytree({
                        source: scope.sc
                    });
                });
            }
        }
    });

Jquery loads the fancy tree onto the page (so the directive is being called) but without an data. I also bind the data to a table on the page just to make sure its loading ok and that works. What am I missing?

Is this the correct way to do this?

2
  • what is the value of scope.sc? Commented Aug 4, 2014 at 5:28
  • scope.sc is a json array of parent/children to load into the tree, just key, title and children (if any) for each item Commented Aug 4, 2014 at 5:39

2 Answers 2

5

You could just let the directive wait till the data is retrieved. Your watch might get executed once before the data is retrieved, so just check for the value in the watch to make sure it it has been retrieved already. Something like this:-

        link: function (scope, element, attrs) {

           var unwatch =  scope.$watch('sc', function(v) {
               if(v){ // Check if you got the value already, you can be more specific like angular.isObject/angular.isArray(v)
                 unwatch(); //Remove the watch
                 $(element).fancytree({
                    source: v //or same as scope.sc
                 });
               }
            });

You could also use ng-if directive on the directive element, provided your directive is of lesser priority than ng-if. You r directive will not render until sc gets defined.

  <div tree-view ng-if="sc"></div>

With 1.3.x you could even make use of 1 time binding.

 <div tree-view ng-if="::sc"></div>
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, this works. you're right, the watch was being executed once before the data was retrieved. How does calling unwatch() inside the if remove the watch?
@garethb scope.$watch retuns a function that when executed removes that particular watch.
ng-if works perfectly with one more scope variable i.e dataLoaded which is false by default, and on ajax success set it as true.
4

You don't need to watch the scope in the directive, so this should be the code for the directive's link method:

link: function (scope, element, attrs) {
    $(element).fancytree({
         source: scope.sc
    });
}

You might also want to call the AJAX in the route's resolve, to have it ready when the controller starts. Makes the code much cleaner.

2 Comments

I thought I didn't need the watch initially as well, but when I set the debugger to break on the $(element).fancytree({, scope.sc is undefined.
Hmmm...if you put the AJAX call in the resolve, it should most likely work, but I'm wondering why is scope undefined otherwise.

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.