【问题标题】:AngularJS - Cannot change directive template dynamically in link functionAngularJS - 无法在链接函数中动态更改指令模板
【发布时间】:2017-08-31 06:04:25
【问题描述】:

注意

接受的解决方案将破坏带有replace: true 的指令:HTML 不会被替换,正在使用的特定 CSS 选择器将不再工作,等等。


我希望我的指令通过观察作为属性从父控制器接收到的字符串来动态更改其模板,所以我使用了来自this answer$compile 和来自this small part of an interesting tutorial$observe,但是唉 它不起作用,如 this plunkr 所示。

关于错误

如果在脚本中 AngularJS 之前包含 jQuery,replaceWith 调用会抛出以下错误:

TypeError: Cannot read property 'ownerDocument' of undefined

但是如果我删除 jQuery,强制 AngularJS 使用它的 jqLit​​e,同样的部分会抛出这个错误,让像我这样的 jQuery 不可知论者更清楚:: p>

TypeError: Failed to execute 'replaceChild' on 'Node': parameter 1 is not of type 'Node'.

即使我很清楚我没有将有效的“节点”类型对象传递给replaceWith,我也不知道如何处理这种情况,因为我期待$compile 完成这项工作.

我唯一知道的是console.log(tplContent) 看起来像这样(我的承诺是对的吗?):

Object
{
  config: Object
  data: "<script type="text/ng-template" id="templateId.html">
  ↵  <p>TEMPLATE A</p>
  ↵</script>"
  headers: function (d)
  ng339: 10
  status: 200
  statusText: "OK"
}

console.log($compile(tplContent)(scope)) 返回一个数组,其对象与第一个也是唯一的项相同:

[Object]
0: {
  config: Object
  data: "<script type="text/ng-template" id="templateId.html">
  ↵  <p>TEMPLATE A</p>
  ↵</script>"
  headers: function (d)
  ng339: 10
  status: 200
  statusText: "OK"
},
length: 1

我确实想避免使用以下两种备用方法中的任何一种,您知道我在这里做错了什么吗?


后备方案 a.k.a. 不要告诉我这样做

我知道我可以将指令分成两个指令,ng-if 他们像这样:

(function() {
  'use-strict';

  angular.module('app')
  .directive('dynamicTemplateA', dynamicTemplate);

  DynTplCtrl.$inject = ['$http', '$templateCache', '$compile', '$parse'];

  function dynamicTemplate($http, $templateCache, $compile, $parse) {
    var directive = {
      restrict: 'E',
      templateUrl: 'template-a.html',
      scope: {},
      bindToController: {
        tpl: '@',
        i: '='
      },
      controller: DynTplCtrl,
      controllerAs: 'dyntplctrl',
      link: linkFunc
    }

    return directive;

    function linkFunc(scope, el, attrs, ctrl) {}
  }

  DynTplCtrl.$inject = [];

  function DynTplCtrl() {}

})()

(function() {
  'use-strict';

  angular.module('app')
  .directive('dynamicTemplateB', dynamicTemplate);

  DynTplCtrl.$inject = ['$http', '$templateCache', '$compile', '$parse'];

  function dynamicTemplate($http, $templateCache, $compile, $parse) {
    var directive = {
      restrict: 'E',
      templateUrl: 'template-b.html',
      scope: {},
      bindToController: {
        tpl: '@',
        i: '='
      },
      controller: DynTplCtrl,
      controllerAs: 'dyntplctrl',
      link: linkFunc
    }

    return directive;

    function linkFunc(scope, el, attrs, ctrl) {}
  }

  DynTplCtrl.$inject = [];

  function DynTplCtrl() {}

})()

然后在controller.html:

<div ng-repeat="i in [1,2,3]">
  <dynamic-template-a ng-if="mainctrl.tpl === 'a'" tpl="{{mainctrl.tpl}}" i="i"></dynamic-template-a>
  <dynamic-template-b ng-if="mainctrl.tpl === 'b'" tpl="{{mainctrl.tpl}}" i="i"></dynamic-template-b>
</div>

我也知道我可以使用ng-include like this

(function() {
  'use-strict';

  angular.module('app')
  .directive('dynamicTemplateA', dynamicTemplate);

  DynTplCtrl.$inject = ['$http', '$templateCache', '$compile', '$parse'];

  function dynamicTemplate($http, $templateCache, $compile, $parse) {
    var directive = {
      restrict: 'E',
      template: '<div ng-include="dyntplctrl.getTemplateUrl()"></div>',
      scope: {},
      bindToController: {
        tpl: '@',
        i: '='
      },
      controller: DynTplCtrl,
      controllerAs: 'dyntplctrl',
      link: linkFunc
    }

    return directive;

    function linkFunc(scope, el, attrs, ctrl) {}
  }

  DynTplCtrl.$inject = [];

  function DynTplCtrl() {
    var vm = this;
    vm.getTemplateUrl = _getTemplateUrl;

    function _getTemplateUrl() {
      return 'template-' + vm.tpl + '.html';
    }
  }

})()

【问题讨论】:

  • 这是第二个后备方案,也是我采用的实际实现我正试图摆脱它;但我面临的第一个问题是其中一个模板无法达到其同一文件夹中directive.less 文件中包含的样式规则(您不会在 plunkr 中找到这个问题,我还没有更新它)。当然,当它不必在两个模板之间动态切换时,它也能够访问它们。

标签: jquery angularjs templates angularjs-directive promise


【解决方案1】:

感谢this question

您需要在替换模板时稍微更改代码:

el.html(tplContent.data);
$compile(el.contents())(scope);

这将替换元素的内容(尽管您需要在此处处理清理),然后在指令的范围内编译模板。

另外,为了测试,我从template-a.htmltemplate-b.html 中删除了&lt;script&gt; 标签。

这是一个forked plunker,上面提到的变化。

【讨论】:

  • 即使我说出来很难过(因为我得不到赏金);这是 ;在我看来正确的答案。赞成
  • @Gargaroz - 请检查此答案是否解决了您的问题。
  • 感谢 @31piy 甚至提供了我一开始找不到的来源。
  • @31piy 这个解决方案只有一个缺陷:它会破坏带有replace: true 的指令,这意味着不会替换任何内容,并且正在使用的特定 CSS 选择器将不再起作用。我在谷歌上搜索了一些关于这个问题的信息,但是很遗憾,即使使用动态模板,我也找不到保持 HTML 被替换的解决方案:您对此有任何见解/建议吗?
【解决方案2】:

您不必将HTML 放在脚本标签中。只需将纯 HTML 存储在您的文件中,例如

模板-a.html

<p>TEMPLATE A</p>

并稍微修改您的代码以实现您想要的。

       function(tplContent) {
           var content = $compile(tplContent.data)(scope);
           if(el[0].childNodes.length){
             el[0].removeChild(el[0].childNodes[0]);
           }
          el.append(content);
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    • 2014-07-05
    • 2013-01-14
    相关资源
    最近更新 更多