$templateCache 在指令中未定义,尽管我在 $http 调用中设置了`{cache: $templateCache}`

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

我有两个 html 页,snippet1.htmlsnippet2.html。我想在我的指令中使用它们。因为我要使用单个指令添加多个模板。

我通过在 <script> 标签内添加 html 模板来尝试这个东西,并像下面那样给它们 type="text/ng-template"

<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>

然后我使用$templateCache.get('snippet1.html')。此实施工作正常。

但在我的例子中,我需要从 html 本身加载它们,所以我决定通过 ajax 加载模板并制作 $http cache: $templateCache

Working JSFiddle

运行块

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

但在我的控制器中 $templateCache.get('snippet1.html') 未定义。

我的问题是,当我在 <script>' tag & Why it don't work when I html inside$templateCachewhile making$http` ajax 调用中声明模板时,为什么它可以工作?

Plunkr With Problem

谁能帮我解决这个问题?或者我在代码中遗漏了任何内容。

帮助将不胜感激。谢谢。

这是一个有趣的问题,我可以提供一个有趣的解决方法以及我对正在发生的事情的看法。我认为可能存在更好的解决方案,但找到这样的解决方案也被证明是一个挑战。尽管如此,我认为主要问题只是您的 console.log($templateCache.get('snippet1.html')) 正在返回 undefined,因为您的 $http.get 没有首先解决的竞争条件。

检查 api 的 $templateCache,我找不到任何有用的方法来了解何时解析通过 ajax 请求的模板。要查看简单问题,运行 在您的指令中查看有关当前存储在 $templateCache

中的一些基本信息
console.log($templateCache.info())

结果为

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

为了观察问题的核心,运行 指令中有相同的 JS,但有这样的超时

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

结果为

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

很有趣,所以它们就在那里...但是现在要处理它们是一个挑战。我精心设计了以下解决方法,至少可以暂时为我们提供一些帮助。将 $q$rootScope 注入到你的 .run 函数中

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
    })
  }]
); 

检查这个,你会注意到我在我们的 $rootScope 上放置了一个任意的 var 这样的 $rootScope.templateCache 是为了在我们的 $watch 上放置一个 $watch指示。然后在我们的指令中,当我们知道 $rootScope.templateCache 上有一个值时,让我们调用我们的 $templateCache,表明 $q 服务已经解决了我们的承诺

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

嘿,看!我们的模板指令已正确呈现。 scope.$parent.$parent 看起来怪怪的是因为在这个指令中,我们已经隔离了我们的 scope,现在需要爬一些梯子来获得定义在 $rootScope.

上的值

我希望我们能找到更简洁更简洁的方法吗?当然!但是,希望这可以确定 为什么 会发生这种情况,以及一种可能的方法来起床 运行 宁现在。下面提供了工作 plunker。

Plunker Link

编辑

这是解决此问题的完全不同的方法,它涉及手动引导

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

这是预期的行为。当您在脚本标记中包含模板时,angular 会在 bootstrap 过程中找到它,并在任何代码运行之前将其添加到缓存中。这就是它在您的指令中可用的原因。

当您使用 $templateCache.put()(或使用 $http.get 检索您在代码中指定的 html 文件时,angular 必须使用 ajax 来解析模板。虽然请求是 "in flight",但模板缓存对此一无所知 - 文件仅在收到响应后才添加到模板缓存中。

由于您的指令作为第一个摘要周期的一部分运行(在启动时),缓存中永远不会有任何远程文件,因此您会看到您看到的错误。

"correct" 做你想做的事情的方法是不直接使用 $templateCache,而是使用 $http 服务来请求远程模板。如果原始响应有 returned,$http 只会为您调用 $templateCache.get。如果没有,它将 return 与原始 $http 请求生成的承诺相同。

这样做,就不需要使用$timeout 或$watch。该模板将在使用 promises 可用后立即编译。

myApp.controller('test',function(){})
    .directive("myTemplate", function ($http, $compile) {
    return {
        restrict: 'EA',
        scope: {
            template: '&?myTemplate',
            src: '&?'
        },
        link: function(scope, element, attrs) {
            $http.get(scope.template() || scope.src()).then(function(result) {
                element.append($compile(result.data)(scope));
            });
        }
    };
});

<my-template src="snippet1.html"></my-template>

<div my-template="snippet1.html"></div>

Here is a working Plunk

编辑:没有 $compile 和 $http

的替代方案
myApp.controller('test',function(){})
    .directive("myTemplate", function ($http, $compile) {
    return {
        restrict: 'EA',
        scope: {
            snippets: '='
        },
        template: 'snippet1.html',
        link: function(scope, element, attrs) {

        }
    };
});

关于你的最后一个问题(为什么你必须使用 [1] 来获取 html - $http 服务不只在缓存中存储 html - 它存储一个数据结构可能包含一个承诺或元素(如果从脚本标签加载)。因为它知道它放入了什么,所以它知道如何取出它。当你短路时你只是在猜测。

长话短说 - 不要使用 $templateCache 自行解析模板。

编辑:来自 $http 的代码演示了可能存储在缓存中的不同类型的数据。

if (cache) {
    cachedResp = cache.get(url);
    if (isDefined(cachedResp)) {
      if (isPromiseLike(cachedResp)) {
        // cached request has already been sent, but there is no response yet
        cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
      } else {
        // serving from cache
        if (isArray(cachedResp)) {
          resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
        } else {
          resolvePromise(cachedResp, 200, {}, 'OK');
        }
      }
    } else {
      // put the promise for the non-transformed response into cache as a placeholder
      cache.put(url, promise);
    }
  }