【问题标题】:Include different angular directives in ng-repeat在 ng-repeat 中包含不同的角度指令
【发布时间】:2026-02-23 02:30:02
【问题描述】:

我有几个角度指令。
我想根据类型将它们包含在带有 ng-repeat 的手风琴指令中。
我希望它是可重用的,所以我不希望手风琴知道它正在渲染的指令类型。
这就是我不想在手风琴内部使用 ng-switch 指令的原因。 这是一个非常简化的演示。实际上,这些指令将具有自己的属性,以便 Angular 进行编译。

var testApp = angular.module('testApp', []);
(function() {
  function Element1() {
    return {
      template: '<span>hello</span>',
      restrict: 'E',
      replace: true
    }
  }
  testApp.directive('elementOne', Element1);
}());

(function() {
  function Element2() {
    return {
      template: '<span>world</span>',
      restrict: 'E',
      replace: true
    }
  }
  testApp.directive('elementTwo', Element2);
}());

(function() {
  function Accordion() {
    return {
      template: '<ul><li ng-repeat="element in elements"><button>Toggle</button> Here should be the directive</li></ul>',
      restrict: 'E',
      replace: true,
      controller: function($scope) {
        $scope.elements = [{
          type: 1
        }, {
          type: 2
        }];
      }
    }
  }
  testApp.directive('elementAccordion', Accordion);
}());
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="testApp">
  <element-accordion></element-accordion>
</div>

谢谢!

【问题讨论】:

  • 是否应该将 elements 作为属性传递给 elementAccordion 指令?
  • @hansmaad 是的,我跳过它以简化示例。

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


【解决方案1】:

几周前我构建了一些非常相似的东西。我想渲染一个指令列表并根据数组中元素的类型选择指令类型(就像你一样)。我试图避免使用ngSwitch,因为我有很多指令并且想要添加新指令而不每次都更改ngSwitch 语句。让我展示一下我所做的简化版本。我希望这将帮助您构建自己的版本。

首先,我们需要一个元素指令的全局目录和一个辅助函数来注册新指令。在我的例子中,辅助函数甚至 创建 指令,但为了简单起见,我将跳过它:

angular.module("app").provider('elementDirectory', function() {        
  return {
    elements: [],
    $get: function () {
      return {
        elements: this.elements
      }
    }
  };
});

function registerElementDirective(type, directiveName) {
  angular.module("app").config(['elementDirectoryProvider', function (elementDirectoryProvider) {
    elementDirectoryProvider.elements.push({
      type : type,
      directive : directiveName
    });
  }]);
}

现在,我们可以像这样创建一些指令:

angular.module("app").directive('elementA', function() {
    return {
      template: '<span>ElementA</span>',
    }
});
registerElementDirective('A', 'element-a');

最有趣的部分是我称为elementSwitch 的指令。它接受一个元素数组并为每个元素动态添加一个angular.element。因此,它为elementDirectoy 中的每个元素创建原型,并对更改使用clone() 方法。 (我认为我们可以跳过这部分,这是一个优化)。

angular.module('app').directive('elementSwitch', elementSwitch);    
elementSwitch.$inject = ['$compile', 'elementDirectory'];
function elementSwitch($compile, elementDirectory) {

  var prototypes = {};
  elementDirectory.elements.forEach(function (e) {
    var directiveElem = angular.element('<' + e.directive + '>');
    prototypes[e.type] = directiveElem;
  });

  return {
    scope: {
      elements: '=elementSwitch'
    },
    link: function (scope, elem, attr) {

      var childScopes = [];
      scope.$watchCollection('elements', function (newValue, oldValue) {
        childScopes.forEach(function (s) {
          s.$destroy();
        });
        childScopes = [];
        elem.empty();

        newValue.forEach(function (element, index) {
          var childScope = scope.$new();
          childScopes.push(childScope);
          var directiveElem = prototypes[element].clone();
          directiveElem.attr('element', '_elements[' + index + ']');
          $compile(directiveElem)(childScope);
          elem.append(directiveElem);
        });
        scope._elements = newValue;
      });
    }
  }
}

这是一个完整的例子:https://codepen.io/hansmaad/pen/oLvRmj

它不会做你想做的事情,但你应该知道如何实现你的目标。

【讨论】:

  • 非常有趣的方法。当你这样做时:directiveElem.attr('element', '_elements[' + index + ']');是复制 ng-repeat 的功能,对吗?它使用数组中的相应数据公开一个“元素”属性。
  • @airnan 是的,它类似于 ngRepeat 所做的。