Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$templateCache is undefined In directive though I set `{cache: $templateCache}` in $http call

I have two html pages, snippet1.html & snippet2.html. I want use them inside my directive. Because I'm going to add multiple template by using single directive.

I tried this thing with by adding html templates inside <script> tag & gave type="text/ng-template" to them like Below.

<script type="text/ng-template" id="snippet1.html">
    <div>Here is Snippet one</div>
</script>

<script type="text/ng-template" id="snippet2.html">
    <div>Here is Snippet two</div>
</script>

And then I use $templateCache.get('snippet1.html'). This implementation is working Fine.

But in my case I need to load them from html itself, so I decided to load template by ajax and make $http cache: $templateCache

Working JSFiddle

Run Block

myApp.run(['$templateCache','$http', function($templateCache, $http){ 
  $http.get('snippet1.html',{ cache : $templateCache }); 
  $http.get('snippet2.html',{ cache : $templateCache }); 
}]);

But inside my controller $templateCache.get('snippet1.html') is undefined.

My question is, Why it is working while i declared template inside <script>' tag & Why it don't work when I html inside$templateCachewhile making$http` ajax call?

Plunkr With Problem

Can anyone help me out with this issue? Or I'm missing anything in code.

Help would greatly appreciated. Thanks.

like image 693
Pankaj Parkar Avatar asked Jan 08 '23 21:01

Pankaj Parkar


1 Answers

This is an interesting issue, and I can offer an interesting workaround and my thoughts on what is going on. I think a better solution may exist, but finding such solutions also proved to be a challenge. Nonetheless, I think the main issue is simply your console.log($templateCache.get('snippet1.html')) is returning undefined because of the race condition with your $http.get's not resolving first.

Examining the api for $templateCache, I couldn't find any sort of helpful way of knowing when templates resolve requested via ajax. To see the simple issue, run this in your directive to see some basic information about what is currently stored in your $templateCache

console.log($templateCache.info())

With the result of

Object {id: "templates", size: 0}

For observing the core of the issue, run the same JS in the directive, but with a timeout as such

setTimeout(function() {
    console.log($templateCache.info())
}, 1000);

With the result of

Object {id: "templates", size: 2}

Interesting, so they're in there... but getting a handle on them is now the challenge. I crafted the following workaround to at least give us something for now. Inject $q and $rootScope into your .run function as such

myApp.run(['$templateCache', '$http', '$q', '$rootScope', function($templateCache, $http, $q, $rootScope){ 
    $q.all([
        $http.get('snippet1.html',{ cache : $templateCache }),
        $http.get('snippet2.html',{ cache : $templateCache }) 
    ]).then(function(resp){
        $rootScope.templateCache = resp
    })
  }]
); 

Examining this, you'll notice I place an arbitrary var on our $rootScope as such $rootScope.templateCache for the purpose of placing a $watch on it in our directive. Then in our directive, let's call into our $templateCache when we then know there is a value on $rootScope.templateCache, indicating the $q service has resolved our promises as such

link: function(scope, element, attrs) {
    scope.$parent.$parent.$watch('templateCache', function(n, o) {
        if(n) {
            element.append($compile($templateCache.get('snippet1.html')[1])(scope));
        }
    });
}

And hey look! Our template directive is correctly rendered. The hacky looking scope.$parent.$parent is because in this directive, we have isolated our scope and now need to climb some ladders to get the value defined on $rootScope.

Do I hope we can find a cleaner more consise way? Of course! But, hopefully this identifies why this is happening and a possible approach to get up and running for now. Working plunker provided below.

Plunker Link

Edit

Here is a completely different approach to solve this which involves manual bootstrapping

var providers = {};

var $injector = angular.injector(['ng']);

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

$injector.invoke(function($http, $q, $templateCache, $document) {
    $q.all([
        $http.get('snippet1.html',{ cache : $templateCache }),
        $http.get('snippet2.html',{ cache : $templateCache }) 
        ]).then(function(resp){
            providers.cacheProvider = $templateCache;
            angular.bootstrap($document, ['myApp']);
        });
    });

myApp
.controller('test',function() {
})
.directive('myTemplate', function ($templateCache, $compile) {
    return {
        restrict: 'EA',
        scope: {
            snippets: '='
        },
        link: function(scope, element, attrs) {
            element.append($compile(providers.cacheProvider.get('snippet1.html')[1])(scope));
        }
    };
});

Updated Plunker

like image 70
scniro Avatar answered Jan 22 '23 02:01

scniro