9

I am trying to create a directive that would load a template. The template will then be cached so the second time you click on the element it would not try to load it but instead get recently loaded value from $templateCache.

I am noticing that in case of the cache hit I don't get any response from $http.get() method.

<html ng-app="website">
<body ng-controller="MyController">
    <a href='#' ng-click="load()">Click Event</a>
    <a href='#' click-load>Click Directive</a>

</body>
</html>​

angular.module('website', []).directive('clickLoad', function($q, $http, $templateCache) {
    return function(scope, element, attrs) {
        function loadData() {
            $http.get('http://fiddle.jshell.net', {
                cache: $templateCache
            }).then(function(result) {
                alert('loaded ' + result.data.length + " bytes");
            });

        }
        element.bind('click', loadData);
    };
});


function MyController($scope, $http, $templateCache) {
    $scope.load = function() {
        $http.get('http://fiddle.jshell.net', {
            cache: $templateCache
        }).then(function(result) {
            alert('loaded ' + result.data.length + " bytes");
        });
    }
}

I've created a fiddle simulating my scenario:

http://jsfiddle.net/3ea64/

Note that you can click Click Event link as many times as you want, however "Click Directive" link only works once if you clicked it first, it doesn't work at all if you click "Click Event" link first.

Any ideas are greatly appreciated.

Sebastian Piskorski
  • 4,026
  • 3
  • 23
  • 29
user43685
  • 1,456
  • 3
  • 17
  • 21

2 Answers2

9

I've played a bit around and used caching of AngularJS (describes here: http://docs.angularjs.org/api/ng.$http)

Here is a live demo: http://jsfiddle.net/4SsgA/

I've basically adjusted the $http syntax and use an ng-click directive instead of registering an event listener inside of the directive (just because I like it more :))

HTML:

<html ng-app="website">
<body ng-controller="MyController">
    <a href='#' ng-click="load()">Click Event</a>
    <a href='#' click-load ng-click="loadData()">Click Directive</a>
</body>
</html>​

JS:

angular.module('website', []).directive('clickLoad', function($q, $http, $templateCache) {
    return function(scope, element, attrs) {
        scope.loadData = function() {
            $http({method: 'GET', url: 'http://fiddle.jshell.net', cache: true}).then(function(result) {
                alert('loaded ' + result.data.length + " bytes");
            });
        }
    };
});


function MyController($scope, $http, $templateCache) {
    $scope.load = function() {
        $http({method: 'GET', url: 'http://fiddle.jshell.net', cache: true}).then(function(result) {
            alert('loaded ' + result.data.length + " bytes");
        });
    }
}​
F Lekschas
  • 12,481
  • 10
  • 60
  • 72
  • 1
    Thanks for your response. You are right the problem is in binding. I changed my code to do $scope.$apply just like ng-click and it worked. See here: http://jsfiddle.net/3ea64/1/ – user43685 Nov 01 '12 at 13:32
  • Wow thanks user43685. That apply scope was just the thing I needed to get http working right in my directive – Erich Aug 28 '13 at 16:58
9

I would suggest creating a separate service that will do the job:

YourModule.factory('Utils', ['$q', '$http', '$templateCache', function ($q, $http, $templateCache) {
    var Service = function() {

    };

    Service.prototype.TemplatePromise = function (keyOrUrl) {
        var data = $templateCache.get(keyOrUrl);

        if (data) {
            return $.when(data);
        } else {
            var deferred = $.defer();

            $http.get(keyOrUrl, { cache: true }).success(function (html) {
                $templateCache.put(keyOrUrl, html);

                deferred.resolve(html);
            });

            return deferred.promise;
        }
    };

    return new Service();
}]);

Using this approach will add flexibility and isolation to the way you get templates, most probably you would want it to be done your own independent way...

Lu4
  • 14,873
  • 15
  • 79
  • 132
  • 3
    You don't need to use `$q` inside `$http.get()` as `$http` already returns a `promise`: `return $http.get(keyOrUrl, { cache: true }).success(function (html) { $templateCache.put(keyOrUrl, html); return html; });` – GFoley83 Jul 07 '14 at 01:11