当指令模板的各个部分存在各自的交叉内容时,替换它们

Replacing parts of directive template when their respective transclusion content is present

本文关键字:替换 指令 个部 存在      更新时间:2023-09-26

我正在尝试执行以下操作:

如果我添加<my-custom-directive></<my-custom-directive>

它应该扩展到

<div class="my-custom-container">
   <label class="my-custom-label">Fallback</label>
   <input class="my-custom-input"/>
</div>

这可以通过在DDO中将上述设置为CCD_ 2和CCD_。

如果我在HTML中添加以下内容:

<my-custom-directive>
   <my-custom-label class="users-custom-class"><span>Custom content</span><my-custom-label>
</<my-custom-directive>

它应该扩展到

<div class="my-custom-container">
   <label class="my-custom-label users-custom-class"><span>Custom content</span></label>
   <input class="my-custom-input"/>
</div>

这意味着,如果用户想要提供自定义的<label><input>等,我们使用transclusion,transclusion的内容将替换原始模板中的相应槽,类似于replace:true指令将如何用其模板替换自身。

我无法组合replace和transclusion功能。


到目前为止,我所拥有的(某些工作状态)如下:

angular.module('test', [])
  .directive('transTest', function() {
    return {
      transclude: {
        lab: '?labelTest',
        inp: '?inputTest'
      },
      replace: true,
      template: '<div class="container"><label ng-transclude="lab">Fallbacl label</label><input type="text" placeholder="fallback" ng-transclude="inp"></div>',
      link: function(scope, element, attrs, ctrl, transclude) {
        console.log(transclude())
      }
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.js"></script>
<div ng-app="test">
  <div trans-test class="test">
    <label-test>test label</label-test>
    <input-test>test input</input-test>
  </div>
</div>

正如你所看到的,transcluded内容进入了包含translate元素的内部,而不是替换它。我已经阅读了源代码注释、文章,还检查了ui bootstrap accordon的实现,并尝试了transclude:'element',但它在DOM中只留下了一个注释。

transclusion、replace等是我发现的可用选项,它们提供了与我试图实现的功能类似的功能。但他们似乎打得不好。如果可能的话,实现这种角度功能的正确方法是什么。。?

看来我终于成功了。该解决方案由两部分组成:

  1. 使用类似于回退(默认)的模板为transclude slot元素定义指令。

    这样做的主要原因是在DDO中设置replace:true时,利用angular的内置功能将属性复制到templates根元素。我不想在链接功能中手动操作
    另一个原因是,它允许您添加额外的功能,如默认模板中不需要的交叉

  2. 第二步是不在模板中定义ng-transclude指令,而是使用传递给linktransclude函数来访问各个插槽的已包含内容,并在存在已包含内容的情况下用已包含内容替换相应元素(使用transclude.isSlotFilled()

好吧,这不容易让我理清思路,也不容易解释。希望下面的演示能比文字更好地解释它:

angular.module('test', [])
  .directive('transTest', function() {
    return {
      replace: true,
      transclude: {
        lab: '?labelTest',
        inp: '?inputTest'
      },
      template: '<div class="test-parent"><label class="fallback-label">Fallback </label><br><input type="text" class="fallback-input"></div>',
      link: function(scope, element, attrs, ctrl, transclude) {
        if (transclude.isSlotFilled('lab')) {
          var label = transclude(angular.noop, null, 'lab');
          element.find('label').replaceWith(label);
        }
        if (transclude.isSlotFilled('inp')) {
          var input = transclude(angular.noop, null, 'inp');
          element.find('input').replaceWith(input);
        }
      }
    }
  }).directive('labelTest', function($compile) {
    return {
      template: '<label class="fallback-label ng-transclude">Fallback </label>',
      replace: true,
      transclude: true
    }
  }).directive('inputTest', function($compile) {
    return {
      template: '<input type="text" class="fallback-input">',
      replace: true
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.js"></script>
<div ng-app="test">
  <div trans-test class="test">
    <label-test class="custom-label">Custom content</label-test>
    <input-test class="custom-input" placeholder="custom"></input-test>
  </div>
  <br>
  <br>
  <div trans-test class="test">
  </div>
</div>

您可能正在寻找一个更优雅的解决方案,但您可以使用指令的控制器$transclude函数来了解transclusion-slot是否已填充。

controller: function($transclude, $scope) {
  $scope.fallback = !$transclude.isSlotFilled('lab');

然后,使用这些信息构建模板。

template: '<div class="container">'
  <label ng-if="fallback === true">Fallback label</label>'
  <div ng-if="fallback === false" ng-transclude="lab"></div>'

但是,如果您能够完全定义要传输的内容,

<label-test>
    <label>test label</label>
</label-test>

替换目标内容可能比替换整个元素更有意义:

template: '<div class="container">'
  <div ng-transclude="lab">'
    <label>Fallback label</label>'
  </div>'

下面是一个展示两种方法的plunker:http://plnkr.co/edit/EEq5vovFrSW7kG81yWuf?p=preview