开发者问题收集

AngularJS - 无法在链接函数中动态更改指令模板

2017-04-05
1137

注意

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


我希望我的指令通过观察从父控制器接收的作为属性的字符串来动态更改其模板,因此我使用了 此答案 中的 $compile 以及 这个有趣的教程的一小部分 中的 $observe ,但 可惜 它不起作用,如 这个 plunkr

关于错误

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

TypeError: Cannot read property 'ownerDocument' of undefined

但是 如果我删除 jQuery,强制 AngularJS 使用其 jqLit​​e,同一部分会抛出此错误, 让像我这样的完全 jQuery 不可知论者更清楚

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

即使我很清楚我没有将有效的“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

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


后备方法,也就是不要告诉我这样做

我知道我可以将指令拆分为两个指令,然后像这样 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 像这样 :

(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';
    }
  }

})()
2个回答

感谢 这个问题

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

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

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

此外,为了进行测试,我从 template-a.htmltemplate-b.html 中删除了 <script> 标签。

这是一个 分叉的 plunker ,它具有上述更改。

31piy
2017-06-08

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

template-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);
        }
Himanshu Mittal
2017-06-12