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

Posted

技术标签:

【中文标题】AngularJS - 无法在链接函数中动态更改指令模板【英文标题】:AngularJS - Cannot change directive template dynamically in link function 【发布时间】: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';
    
  

)()

【问题讨论】:

为什么不这样做***.com/a/40230128/4315380 这是第二个后备方案,也是我采用的实际实现我正试图摆脱它;但我面临的第一个问题是其中一个模板无法达到其同一文件夹中directive.less 文件中包含的样式规则(您不会在 plunkr 中找到这个问题,我还没有更新它)。当然,当它不必在两个模板之间动态切换时,它也能够访问它们。 【参考方案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);
        

【讨论】:

以上是关于AngularJS - 无法在链接函数中动态更改指令模板的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS - 如何动态更改组件控制器?

动态设置 ui-sref Angularjs 的值

无法更改页面中的对象值,但在控制台日志中显示为工作(angularjs)

AngularJs:如何对指令创建的动态元素应用表单验证

如何使用angularjs添加动态过滤功能来处理高级搜索?

无法在 AS3 中更改动态文本