【问题标题】:Angularjs: transclude directive templateAngularjs:transclude 指令模板
【发布时间】:2013-10-01 15:36:48
【问题描述】:

在以下情况下如何使用嵌入。目的是在 html(部分)文件中使用标记,而不是在模板中(在指令中)定义它。

我在这里找到了一个很棒的树指令。 (source) 原文:http://jsfiddle.net/n8dPm/

我没有在指令中定义模板,而是尝试使用嵌入的内容。我还将 Angular 更新为 1.2.0.rc2。 更新:http://jsfiddle.net/aZx7B/2/

出现以下错误

TypeError: 对象 [object Object] 的属性“$transclude”不是 功能

代码:

module.directive("tree", function($compile) {
    return {
        restrict: "E",
        transclude: true,
        scope: {family: '='},
        template:       
            '<ul>' + 
                '<li ng-transclude></li>' +
                '<li ng-repeat="child in family.children">' +
                    '<tree family="child"></tree>' +
                '</li>' +
            '</ul>',
        compile: function(tElement, tAttr) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents);
                }
                compiledContents(scope, function(clone, scope) {
                         iElement.append(clone); 
                });
            };
        }
    };
});

<div ng-app="myapp">
    <div ng-controller="TreeCtrl">
        <tree family="family">
            <p>{{ family.name }}</p>
        </tree>
    </div>
</div>

编辑:

在大卫的建议下,做了一些改动。 http://jsfiddle.net/aZx7B/3/ 现在,它打印出来了,家长。改变,family -> treeFamily 没用

【问题讨论】:

  • 这有几个问题:您在嵌入中引用了 family.name,但 family 是指令范围的一部分,并且不可用。您必须使用 treeFamily.name。此外,您的嵌套树不会包含嵌入的内容。如果你使用提供给编译函数(第三个参数)的 transclude 函数而不是 ngTransclude,你可能会走得更远。
  • 感谢大卫,更新一些变化。
  • 我一直在做类似的事情,想将我的 html 保存在模板中。但是除非我在链接函数中手动编译它,否则递归将不起作用(无限摘要 - 我认为)。我真的很想知道为什么会这样,所以我实际上可以根据知识做出编码决策,而不是“因为它就是这样”
  • 我编辑的答案是否解释了为什么模板要正确输出?使用我最后一个版本的代码,您应该能够将任何您想要的内容传递到您的自定义指令中,从而使用它们自己的模板来诱导其他自定义指令。

标签: javascript angularjs


【解决方案1】:

您还需要在模板中输出族的名称: http://jsfiddle.net/roadprophet/DsvX6/

module.directive("tree", function($compile) {
    return {
        restrict: "E",
        transclude: true,
        scope: {family: '='},
        template:       
            '<ul>' + 
                '<li ng-transclude></li>' +
                '<li ng-repeat="child in family.children">' +
                    '<tree family="child">{{family.name}}</tree>' +
                '</li>' +
            '</ul>',
        compile: function(tElement, tAttr, transclude) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents, transclude);
                }
                compiledContents(scope, function(clone, scope) {
                         iElement.append(clone); 
                });
            };
        }
    };
});

编辑

您也可以这样做来简化:http://jsfiddle.net/roadprophet/DsvX6/2/

<div ng-app="myapp">
    <div ng-controller="TreeCtrl">
        <tree family="treeFamily">           
        </tree>
    </div>
</div>


module.directive("tree", function($compile) {
    return {
        restrict: "E",
        transclude: true,
        scope: {family: '='},
        template:       
            '<ul>' + 
                '<li ng-transclude></li>' +
                '<p>{{ family.name }}</p>' + 
                '<li ng-repeat="child in family.children">' +
                    '<tree family="child"></tree>' +
                '</li>' +
            '</ul>',
        compile: function(tElement, tAttr, transclude) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents, transclude);
                }
                compiledContents(scope, function(clone, scope) {
                         iElement.append(clone); 
                });
            };
        }
    };
});

编辑 虽然问题的根源相同。没有模板被传递给内部树指令。 http://jsfiddle.net/roadprophet/DsvX6/3/

<div ng-app="myapp">
    <div ng-controller="TreeCtrl">
        <tree family="treeFamily">           
                <p>{{ family.name }}</p>
        </tree>
    </div>
</div>

 template:       
            '<ul>' + 
                '<li ng-transclude></li>' +
                '<li ng-repeat="child in family.children">' +
                    '<tree family="child"><div ng-transclude></div></tree>' +
                '</li>' +
            '</ul>'

【讨论】:

  • 感谢您的帮助,但这不是我需要的。我需要在 html 文件中定义template,并转换到我使用ng-transclude 的位置。因此,在我的示例中,部分模板是{{family.name}},但它可能远不止于此(多行)。
  • stackoverflow.com/questions/14509959/… 就是一个例子。就我而言,递归导致它不起作用。我想要获得的优势是同一指令具有多个模板,并且易于维护(比 JS 更容易更改模板中的代码)
  • 新的“编辑”有意义吗?只需重用作为模板传入的模板,即可递归使用 tree 指令。似乎您可以根据自定义属性进行家庭结构。并且模板可以是你想要的树的不同用途。
  • 这似乎满足您的需求/要求。对最初的误解深表歉意。
  • 斯蒂芬。太好了,我想这正是我正在寻找的。让我再做一些测试,然后再回来陪你。谢谢
【解决方案2】:

您想针对 parent 范围编译转置的 DOM;您可以使用指令控制器定义中的可注入 $transclude 函数自动执行此操作:

module.directive("tree", function($compile) {
  return {
    restrict: "E",
    transclude: true,
    scope: { family: '=' },
    template: '<ul>' + 
                '<li ng-repeat="child in family.children">' +
                  '<tree family="child">' +
                    '<p>{{ child.name }}</p>' +
                  '</tree>' +
                '</li>' +
              '</ul>',
    controller: function($element, $transclude) {
      $transclude(function(e) {
        $element.append(e);
      });
    },
    compile: function(tElement, tAttr, transclude) {
      var contents = tElement.contents().remove();
      var compiledContents;
      return function(scope, iElement, iAttr) {
        if(!compiledContents) {
          compiledContents = $compile(contents);
        }
        compiledContents(scope, function(clone) {
          iElement.append(clone);
        });
      };
    }
  };
});

这允许您在根模板中使用 parent 范围属性 treeFamily(还要注意上面指令模板中 child 的使用):

<div ng-app="myapp">
  <div ng-controller="TreeCtrl">
    <tree family="treeFamily">
      <p>{{ treeFamily.name }}</p>
    </tree>
  </div>
</div>

您可以在此处查看示例:http://jsfiddle.net/BinaryMuse/UzHeW/

【讨论】:

  • 布兰登,谢谢。但是,'

    {{ child.name }}

    ' 正在输出树名(这是指令的一部分)。这违背了目的。我实际上想要html中的那部分。这是一个简单的案例,但我可能会在 html 中添加更多标记。我只需要访问 html 中的树 node,并针对此呈现 html 中定义的标记。因此,我可以重用该指令,但可以轻松更改 html 中包含的标记。我认为这是嵌入的目的之一。无论如何,如果我从指令中删除标记,则没有任何输出。jsfiddle.net/UzHeW/8 如果我还不清楚,请告诉我。
【解决方案3】:

晚会很晚了。我在一个项目中需要这个,所以在深入研究它并找到其他很好的方法和方向之后,终于想出了这个:

ng-transclude 指令的代码相同,但添加了少量context 绑定,该指令在每次更改时监视并设置在嵌入的生成范围上。与ng-repeat 相同,但这允许:

  1. 将自定义 ng-transclude 与 ng-repeat 一起使用,无需重写 ng-repeat 和类似 angular/2 模板出口的麻烦。
  2. 让嵌入的内容在从父级接收直接上下文数据的同时继续访问祖级范围。再次与模板插座相同。

增强的 ng-transclude 函数:

return function ngTranscludePostLink(
   ...
  ) {
  let context = null;
  let childScope = null;
  ...
  $scope.$watch($attrs.context, (newVal, oldVal) => {
    context = newVal;
    updateScope(childScope, context);
  });
  ...
  $transclude(ngTranscludeCloneAttachFn, null, slotName);
  ...
  function ngTranscludeCloneAttachFn(clone, transcludedScope) {
     ...                                 
     $element.append(clone);
     childScope = transcludedScope;
     updateScope(childScope, context);
     ...
  }
  ...
  function updateScope(scope, varsHash) {
    if (!scope || !varsHash) {
      return;
    }
    angular.extend(scope, varsHash);
  }
}

它的用法:

应用

<my-list items="$ctrl.movies">
   <div>App data: {{ $ctrl.header }}</div>
   <div>Name:{{ name }} Year: {{ year }} Rating: {{ rating 
          }}</div>
</my-list>

我的列表

<ul>
  <li ng-repeat="item in $ctrl.items track by item.id">
   <div>Ng repeat item scope id: {{ $id }}</div>
   <cr-transclude context="item"></cr-transclude>
  </li>
</ul>

完整指令代码可见here on GitHub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-26
    • 2015-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多