加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

angularjs – ng-repeat内部的ng-transclude正在失去对$transclu

发布时间:2020-12-17 17:45:27 所属栏目:安全 来源:网络整理
导读:我有一个类似列表的容器指令,可以将内容转换为ng-repeat. 模板看起来像这样: div ng-repeat='item in items' div ng-transclude/div/div 用法看起来像这样 my-container foo/my-container 这按预期工作.但是,只要我的模板中的ng-repeat上面有任何指令,例如
我有一个类似列表的容器指令,可以将内容转换为ng-repeat.

模板看起来像这样:

<div ng-repeat='item in items'>
    <div ng-transclude></div>
</div>

用法看起来像这样

<my-container>
    foo
</my-container>

这按预期工作.但是,只要我的模板中的ng-repeat上面有任何指令,例如

<div ng-style='{}'>
    <div ng-repeat='item in items'>
        <div ng-transclude></div>
    </div>
</div>

我的代码抛出

"Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <div ng-transclude="">"

首先看一下Angular代码,看起来Angular没有正确地将$transclude注入设置到ngTransclude控制器中.我将开始挖掘Angular代码,试图找出原因,但如果有人已经知道发生了什么和/或如何解决或解决它,我将非常感激.

这是一个功能齐全的小提琴:http://jsfiddle.net/7BuNj/1/

对于否定案例,这是一个完全无功能的小提琴:http://jsfiddle.net/8BLYG/

解决方法

tl; dr tl; dr tl; dr

看起来Angular递归编译周期不会通过带有链接函数的指令将转换函数线程化.我的ngStyle指令有一个链接函数,因此在ngRepeat编译其模板时,转换功能就丢失了.目前尚不清楚这是预期的行为还是一个错误;我稍后会与Angular团队进行跟进.就目前而言,我暂时修补了我的Angular v.1.2.0副本以替换5593-5594行

: compileNodes(childNodes,(nodeLinkFn && nodeLinkFn.transclude) ? nodeLinkFn.transclude : transcludeFn);

一切都很好.

这个调查

行.为了对非Angular专家(例如,我:)有任何意义,我将对Angular的编译/链接循环的代码进行一些了解.我在这里提供了v1.2.0相关位的超级精简版本供参考(对不起Angular团队,我打破了我想象的相当宗教风格的指导方针,试图让代码尽可能短的代码段;) :

function compileNodes(nodeList,transcludeFn,$rootElement,maxPriority,ignoreDirective,previousCompileContext) {
  ...
  for (var i = 0; i < nodeList.length; i++) {
    ...
    nodeLinkFn = (directives.length)
                 ? applyDirectivesToNode(directives,nodeList[i],attrs,null,[],previousCompileContext)
                 : null;
    ...
    childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !(childNodes = nodeList[i].childNodes) || !childNodes.length)
                  ? null
                  : compileNodes(childNodes,nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
    ...
    linkFns.push(nodeLinkFn,childLinkFn);
    ...
  }
  return linkFnFound ? compositeLinkFn : null;

  function compositeLinkFn(scope,nodeList,boundTranscludeFn) {
    ...
    for(i = 0,n = 0,ii = linkFns.length; i < ii; n++) {
      ...
      nodeLinkFn = linkFns[i++];
      childLinkFn = linkFns[i++];
      if (nodeLinkFn) {
        ...
        childTranscludeFn = nodeLinkFn.transclude;
        if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
          nodeLinkFn(childLinkFn,childScope,node,createBoundTranscludeFn(scope,childTranscludeFn || transcludeFn));
        } else {
          nodeLinkFn(childLinkFn,boundTranscludeFn);
        }
      } else if (childLinkFn) {
        childLinkFn(scope,node.childNodes,undefined,boundTranscludeFn);
      }
    }
  }
}

function applyDirectivesToNode(directives,compileNode,templateAttrs,jqCollection,originalReplaceDirective,preLinkFns,postLinkFns,previousCompileContext) {
  ...
  if (directiveValue = directive.transclude) {
    hasTranscludeDirective = true;
    if (directiveValue == 'element') {
      ...
      $template = groupScan(compileNode,attrStart,attrEnd);
      ...
      childTranscludeFn = compile($template,terminalPriority,replaceDirective && replaceDirective.name,{ ... });
    } else {
      $template = jqLite(jqLiteClone(compileNode)).contents();
      $compileNode.empty(); // clear contents
      childTranscludeFn = compile($template,transcludeFn);
    }
  }
  ...
  // setup preLinkFns and postLinkFns
  ...
  nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
  return nodeLinkFn;

  function nodeLinkFn(childLinkFn,scope,linkNode,boundTranscludeFn) {
    ...
    forEach(controllerDirectives,function(directive) {
      ...
      transcludeFn = boundTranscludeFn && controllersBoundTransclude;
      // puts transcludeFn into controller locals for $transclude access
      ...
    }
    ...
    // PRELINKING: call all preLinkFns with boundTranscludeFn
    ...
    // RECURSION
    ...
    childLinkFn && childLinkFn(scopeToChild,linkNode.childNodes,boundTranscludeFn);
    ...
    // POSTLINKING: call all preLinkFns with boundTranscludeFn
    ... 
  }
}

编译/链接不带插入

本质上,编译周期通过compileNodes对DOM进行深度优先搜索.在每个节点,

>它通过applyDirectivesToNode将该节点上的任何指令编译成nodeLinkFn,该nodeLinkFn可以访问任何已配置的prelink或postlink函数,并接受子compositeLinkFn进行递归
>递归地将节点的子树编译为compositeLinkFn
>将带有子树compositeLinkFn的nodeLinkFn打包到节点级compositeLinkFn中(如果为空,则跳过nodeLinkFn)

编译完成后,您将拥有一个顶级compositeLinkFn,其执行的深度优先与编译进程完全并行,并且每个节点执行所有预链接函数,递归,然后执行所有postlink函数.

编译/链接1级别的转换

只要applyDirectivesToNode命中设置了transclude标志的指令,它就会将指令元素的内容完全编译成与当前编译递归分开的“transclude链接函数”,并用它来注释指令的nodeLinkFn.当封闭的compositeLinkFn最终被执行时,这个注释被读取并作为boundTranscludeFn传递给nodeLinkFn,最终它将被设置为controller $transclude注入,传递给所有prelink和postlink函数,并传递给子链接的递归调用功能. boundTranscludeFn的这种递归线程是关键,因此您可以在transcluding指令的模板内的任何位置访问$transclude.

编译/链接多个级别的转换

现在如果我们在transcluding指令A中转换指令B怎么办?我们想要发生的事情(我假设)是指令A的transclude函数以某种方式提供给指令B的转换内容.毕竟,B的转换内容应该是A的模板中的所有意图和目的.问题是B的转换内容是从A的编译递归分别编译到它自己的转换链接函数中的,它不是A的链接递归的一部分,因此在链接时不会接收A的转换函数.

Angular解决了这个问题:

>另外通过compileNodes递归(而不仅仅是链接递归)线程转换函数
>在编译时将transcludeFn直接传递到transclude内容编译周期
>允许compositeLinkFn从其封闭的compileNodes调用中提取transclude函数

结论

我在原始问题中遇到的问题是通过编译递归来进行转换函数的线程化.链接递归始终正确地将转换函数传递给子链接函数,但是如果当前节点的指令没有链接函数,则编译递归仅向下传递转置函数,无论该指令是否转换:

compileNodes(childNodes,nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);

我的问题爆炸是因为ngStyle创建了一个链接函数,但没有转换.所以突然之间,transclude函数停止在编译循环中进行操作,在ngRepeat编译其子内容时可以使用它. “修复”是将违规行更改为:

compileNodes(childNodes,(nodeLinkFn && nodeLinkFn.transclude) ? nodeLinkFn.transclude : transcludeFn);

基本上,现在它只是停止线程转换函数,如果它被一个新的,更深层嵌套的转换函数替换(这与Angular文档一致,说内容应该被转换为最近的transcluding parent指令).同样,我不确定所有这些都是预期的行为或错误,但希望无论如何它都是有用的转换?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读