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

时间:2015-01-20 22:18:19

标签: javascript angularjs angularjs-directive

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

全文: 我有一个外部指令,其中包含内部指令。诀窍是,这些内部指令也是可以在不知道父母的情况下自己生活的项目。

规则很简单:

  • 如果项目单独使用,则必须通过属性将其配置传递给它。
  • 如果某个项目由其父项包含,则必须通过父属性
  • 将其配置传递给它
  • 我无法将完整的组合HTML写入index.html。它必须在运行中加载。他们是规则。但是,这样做可以解决问题。
  • URL必须是动态的,并且父级必须能够传递此信息而不依赖于范围继承 - 父级可能不在那里。
  • 第三方网站无法告诉我使用JS在哪里指出这些。他们唯一的工具是index.html中的HTML。属性配置很好。

的index.html:

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

OR - index.html也可以是:gorilla可以明确指定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。只有当大猩猩attrs.templateBaseUrl方法使用templateUrl时,我才能跟踪它,attrs.templateBaseUrl它还没有被插值。< / p>

因此,gorilla.link()在插入{{templateBaseUrl}}和kaboom之前运行。 404 at&#34; {{templateBaseUrl}} / gorilla.html&#34;

我该如何避免这种情况?

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

我在每个项目依赖的提供程序中都有这个baseUrl东西,但它与此简化版本具有相同的效果。它必须是解析订单问题。

1 个答案:

答案 0 :(得分:4)

为什么?

您的方法失败,因为templateUrl函数必须在控制器的“编译”阶段之前运行(如果没有,则无法编译模板)。使用嵌套指令,首先运行所有编译函数,然后运行链接函数。我发现下面的图表可以作为使用嵌套指令时“运行时什么时候运行”的参考 - 它来自关于主题的公平in depth article - 一个很好的阅读。

http://www.jvandemo.com/content/images/2014/Aug/cycle-2.png

考虑到这一点,很明显,当你的gorilla指令正在编译时,动物园的链接功能还没有运行,这意味着范围值甚至没有设置,更不用说内插到属性中了。

如何避免

看起来您将不得不自己获取并编译模板。您可以使用angular $templateRequest来确保模板正确缓存。我们可以通过仅使用范围中的值来避免插值是否已经发生(我使用了隔离范围,因为它使事情变得不那么模糊,并且通常是更好的实践,但如果你想要你可以只使用范围继承)。 / p>

免责声明:以下代码已经编写而没有运行它,肯定会包含拼写错误和错误!希望你能看到逻辑......

子指令代码:

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

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

重要的是(再次参考图表!),(zoo)指令必须在其预链接函数中设置模板库的值,否则当子指令运行的链接函数值为undefined

(简体)父指令:

您可以在此处使用类似于子指令,或使用您的原始方法。此代码已简化,以提供在预链接期间如何设置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>