具有动态 templateUrl 方法的嵌套指令失败

Nested directive with dynamic templateUrl method failing

长话短说: 在指令的 templateUrl 方法中使用 attr 值,在使用子指令时未插入 attr。最终结果是文字 {{attrName}}/something.html.

全文: 我有一个外部指令,其中包括内部指令。诀窍是,这些内部指令也是可以在父级不知情的情况下独立存在的项目。

规则很简单:

Index.html:

<div zoo feeding-time="8am" template-base="/templates"></div>

或 - index.html 也可能是:大猩猩可以明确指定 attr 值,因为它不继承

<div gorilla template-base-url="/templates"></div> 

Zoo.html - 它将自己的配置传递给 gorilla

<div gorilla template-base-url="{{templateBaseUrl}}"></div>

动物园-directive.js

angular.module("app").directive("zoo", [function() {
        return {
            restrict: "A",
            scope: true,
            templateUrl: function(element, attrs) {

                // THIS ONE NEVER FAILS BECAUSE ITS NEVER INTERPOLATED

                var base = attrs.templateBaseUrl;
                return base + "/zoo.html";
            },
            link: function(scope, element, attrs) {
                // its my job to give gorilla a templateURL in this case
                scope.templateBaseUrl = attrs.templateBaseUrl;
            }
        };
    }]);

gorilla.html

angular.module("app").directive("gorilla", [function() {
        return {
            restrict: "A",
            scope: true,
            templateUrl: function(element, attrs) {

                // THIS ONLY FAILS WHEN INCLUDED BY ZOO.HTML
                // AND THEN, ONLY SOMETIMES.  RACE CONDITION?  PRIORITY?

                // DOES NOT FAIL WHEN INCLUDED BY INDEX.HTML DIRECTLY

                var base = attrs.templateBaseUrl;
                return base + "/gorilla.html";
            }
        };
    }]);

这行得通。有时。有时,文字 {{templateBaseUrl}} 用于 tempateUrl 方法。我只能追踪到当大猩猩的 templateUrl 方法使用 attrs.templateBaseUrl 时,attrs.templateBaseUrl 它尚未被插值。

因此,gorilla.link(){{templateBaseUrl}} 被插值和 kaboom 之前是 运行。 404 在“{{templateBaseUrl}}/gorilla.html”

我怎样才能避免这种情况?

https://docs.angularjs.org/error/$compile/tpload?p0=%7B%7BtemplateBaseUrl%7D%7D%2Fgorilla.html

我在每个项目依赖的提供者中都有这个 baseUrl 东西,但它与这个简化版本的效果相同。一定是解析顺序问题。

为什么?

你的方法失败了,因为 templateUrl 函数必须在控制器的 'compile' 阶段之前 运行 (如果你没有得到它,你就无法编译模板).对于嵌套指令,所有编译函数首先是 运行,然后是 linking 函数。我发现下图在使用嵌套指令时可作为 'what runs when' 的参考 - 它来自关于该主题的相当 in depth article - 很好的阅读。

考虑到这一点,可以清楚地看到当你的大猩猩指令正在编译时,动物园的link函数还没有运行,这意味着范围值甚至没有已经设置,更不用说插值到属性中了。

如何避免

看来您将不得不自己获取和编译模板。您可以使用 angular 的 $templateRequest 来确保模板被正确缓存。我们可以通过仅使用作用域中的值来避免担心插值是否发生(我使用了隔离作用域,因为它使事情变得不那么模糊,而且通常是更好的实践,但如果你愿意,你可以只使用作用域继承)。

免责声明:以下代码未经运行宁写,肯定会有错别字和错误!希望你能明白其中的逻辑...

子指令代码:

angular.module("app")
.directive("gorilla", function($templateRequest, $compile) {
  return {
    restrict: "A",
    // set up an isolate scope
    scope: {
      tplBaseUrl: '='
    },
    link: {

      pre: function (scope, elem, attr) {
        // Decide if the url is directly set or is dynamic
        var baseUrl = scope.tplBaseUrl ? scope.tplBaseUrl : attr.tplBaseUrl;
        // request the template
        $templateRequest(baseUrl + '/gorilla.html')
        .then(function (response) { 
          tpl = response.data;
          // compile the html, then link it to the scope
          $elem = $compile(tpl)(scope);
          // append the compiled template inside the element
          elem.append($elem);
        });
      },

      post: function (scope, elem, attr){
        // you can put your normal link function code in here.
      }
    }
  };
});

请注意,pre-link 函数的第一行实质上是检查是否有一个范围变量设置为您传递的名称,如果没有,则假设您已经给它一个 url 字符串(因此使用属性值)。这可能不是最好的方法 - 我很想使用两个不同的属性,但这取决于你。

重要的是(再次查看图表!),您的 parent (zoo) 指令 必须 在其pre-link 函数,否则当子指令 运行 的 link 函数时,值将是 undefined.

(简体)父指令:

您可以在此处使用类似于子指令的方法,或者使用您原来的方法。此代码经过简化以举例说明如何在 pre-link.

期间设置 tplBaseUrl
angular.module("app")
.directive("zoo", function() {
  return {
    restrict: "A",
    template: '<div gorilla tpl-base-url="tplBaseUrl"></div>',
    link: {
      pre: function (scope, elem, attr) {
        // make sure it is set here!
        scope.tplBaseUrl = "/templates";
      },
      post: function (scope, elem, attr){
        // link logic
      }
    }
  };
});

最后,如果您要静态设置它,这也应该有效:

<div gorilla template-base-url="/templates"></div>