如何通过transclude将“模板”传递给指令?

时间:2014-07-14 03:07:01

标签: javascript angularjs angularjs-directive angularjs-ng-repeat

演示:http://plnkr.co/edit/TiH96FCgOGnXV0suFyJA?p=preview

我有一个名为myDirective的ng-directive,在指令模板中我有一个使用ng-repeat打印的li标签列表。我想声明li标签的内容作为myDirective声明的一部分,并使用transclude打印所需的text / html内容。这样我可以很好地分离关注点,这样我的指令就不需要知道源项的结构了,调用者负责布局li的内容。

如下所示:

<my-directive source="vm.source">{{label}} and {{id}}</my-directive>

甚至

<my-directive source="vm.source"><a href="#{{id}}">{{label}}</a></my-directive>

ngRepeat的模板(在myDirective中)看起来像

template: '<ul><li ng-repeat="item in source" ng-transclude></li></ul>',

但我无法在hg-repeat内部进行转换。我使用的是角度1.2.19的最新版本。确切地说,trasnclusion工作但不是我在指令级传递的表达式。

请帮忙,谢谢堆!

我无法想出一个更好的问题标题。欢迎你做得更好。

更新:我选择了@pixelbits的答案,因为这就是我的要求。但实际上我使用了@Norguard的方法,因为它更有棱角分明。

2 个答案:

答案 0 :(得分:4)

已转换的内容是根据父作用域的子作用域编译的(也称为转换作用域,但这与您的指令的独立作用域不同)。话虽这么说,您可以在父HTML中指定模板(尽管它在角度的正常用例之外)并且根据指令的隔离范围手动编译它。

<强> HTML

  <body ng-app="myApp">
    <div ng-controller="myController as vm">
      <my-directive source="vm.source">
        <span>{{item.label}} and {{item.id}}</span>
      </my-directive>
    </div>
  </body>

请注意,my-directive的内部HTML引用了&#39; item&#39;必须在指令的范围内定义。

指令

function directive($compile) {
    return {
        restrict: 'E',
        scope: {
            source: '='
        },
        compile: function(element, attr) {
          // in the compile phase, remove the contents 
          // of element so that it is not compiled by angular.
          // We will be manually compiling it in the link function.
           var template = angular.element('<ul><li ng-repeat="item in source">' + element.html() + '</li></ul>');
           element.empty();

           // link function
           return function(scope, element, attr) {
             // append the template
             element.append(template);

             // compile and link the template against the isolated scope.
             $compile(template)(scope);
           }              
        }          
    };
}

Demo Plunker

答案 1 :(得分:2)

杰夫,你的抄送有点倒退。

或者,与你的想法相比,转换工作的方式有点落后。

如果你有一个transcluding指令,并且你把内容放在其中,那么内容是从transclude指令的父节点读取的,而不是指令本身。

对于您的示例,假设:

<div ng-controller="parentController as parent">
    <transcluding-directive>
        {{ key }} {{ val }}
    </transcluding-directive>
</div>

这里有两个问题。

第一个问题是ng-transclude是它所转换内容的兄弟。
他们都会使用parent作为父母范围 被抄送的内容不会从转录指令内部读取数据 这又是关注点,事物的分离;如果transcluding指令具有与您要转录的内容相同的属性名称,那将覆盖被抄送的内容,并导致各种奇怪的行为,因此他们是兄弟姐妹。

出于所有意图和目的,在幕后,它可能表现如下:

<transcluding-directive></transcluding-directive>
<div>{{ transcludedContent }}</div>

然后,内容将被提取,并附加到transcludingDirective.querySelector("[ng-transclude"])找到的节点。

这真的不是它的确切运作方式,但它是你得到的结果(缺少你在指令中自己编译/转录例程)。

当您知道第一个错误时,第二个错误更明显:
{{标签}}和vm的{​​{id}}是vm&#39; $scope个对象的属性。

它们不存在于$scope,因此undefined,这就是您从模板中获取'' + 'and' + ''的原因。 您正在为传递给指令的每个项目创建<li>,但您要为每个项目插入undefinedundefined

您编写的指令应该是一个专门的指令(知道如何构建那种确切类型的指令的指令),

<specific-list-maker items="vm.list"></specific-list-maker>

或一般指令,您将列表提供给

<generic-drawer>
    <ul><li ng-repeat="item in vm.list">{{item.label}} {{item.id}}</li></ul>
</generic-drawer>

...它甚至可能是一个特定的指令,它知道如何在其模板中提供更多通用指令......

 <specific-list items="vm.list"></specific-list>

 <!-- specific-list.html -->
 <generic-drawer>
     <generic-toggle ng-repeat="item in items">{{ item.label }} {{ item.id }}</generic-toggle>
 </generic-drawer>

这是Angular(和Polymer,以及将来的Web Components本身)非常适合的组合。

作为我一直在研究的项目中的一个简单示例,我有过滤掉的元素,如下所示:

<page-type-a></page-type-a>

<!-- page-type-a.html -->
<card-collection cards="page.items"></card-collection>

<!-- card-collection.html -->
<header ><!-- ... header stuff --></header>
<card ng-repeat="card in cards"></card>

<!-- card.html -->
<header><h1>{{ title }}</h1></header>
<blockquote>{{ content }}</blockquote>
<cite>{{ author }}</cite>


<page-type-b></page-type-b>
<!-- page-type-b.html -->
<card-collection cards="page.items"></card-collection>

每个组件只做自己的工作。

还有其他方法可以让它发挥作用(输入&#34;键&#34;属性和&#34;值&#34;属性,并从列表中的每个项目中功能性地提取值在将ng-repeat实例链接到transcluding-directive的内部模板之前,为您的$scope ......或者准备好已转换内容中的值,编写自己的内部列表,在将它插入DOM之前......)...但是这样做,在这种情况下就像听起来一样疯狂。