Recursion in Angular directives

前端 未结 9 1524
不思量自难忘°
不思量自难忘° 2020-11-22 04:24

There are a couple of popular recursive angular directive Q&A\'s out there, which all come down to one of the following solutions:

  • manually incrementally \
9条回答
  •  失恋的感觉
    2020-11-22 04:38

    After using several workarounds for a while, I've repeatedly come back to this issue.

    I'm not satisfied by the service solution since it works for directives that can inject the service but does not work for anonymous template fragments.

    Similarly, solutions which depend upon specific template structure by doing DOM manipulation in the directive are too specific and brittle.

    I have what I believe is a generic solution that encapsulates the recursion as a directive of its own that interferes minimally with any other directives and can be used anonymously.

    Below is a demonstration that you can also play around with at plnkr: http://plnkr.co/edit/MSiwnDFD81HAOXWvQWIM

    var hCollapseDirective = function () {
      return {
        link: function (scope, elem, attrs, ctrl) {
          scope.collapsed = false;
          scope.$watch('collapse', function (collapsed) {
            elem.toggleClass('collapse', !!collapsed);
          });
        },
        scope: {},
        templateUrl: 'collapse.html',
        transclude: true
      }
    }
    
    var hRecursiveDirective = function ($compile) {
      return {
        link: function (scope, elem, attrs, ctrl) {
          ctrl.transclude(scope, function (content) {
            elem.after(content);
          });
        },
        controller: function ($element, $transclude) {
          var parent = $element.parent().controller('hRecursive');
          this.transclude = angular.isObject(parent)
            ? parent.transclude
            : $transclude;
        },
        priority: 500,  // ngInclude < hRecursive < ngIf < ngRepeat < ngSwitch
        require: 'hRecursive',
        terminal: true,
        transclude: 'element',
        $$tlb: true  // Hack: allow multiple transclusion (ngRepeat and ngIf)
      }
    }
    
    angular.module('h', [])
    .directive('hCollapse', hCollapseDirective)
    .directive('hRecursive', hRecursiveDirective)
    /* Demo CSS */
    * { box-sizing: border-box }
    
    html { line-height: 1.4em }
    
    .task h4, .task h5 { margin: 0 }
    
    .task { background-color: white }
    
    .task.collapse {
      max-height: 1.4em;
      overflow: hidden;
    }
    
    .task.collapse h4::after {
      content: '...';
    }
    
    .task-list {
      padding: 0;
      list-style: none;
    }
    
    
    /* Collapse directive */
    .h-collapse-expander {
      background: inherit;
      position: absolute;
      left: .5px;
      padding: 0 .2em;
    }
    
    .h-collapse-expander::before {
      content: '•';
    }
    
    .h-collapse-item {
      border-left: 1px dotted black;
      padding-left: .5em;
    }
    
    .h-collapse-wrapper {
      background: inherit;
      padding-left: .5em;
      position: relative;
    }
    
    
    
      
        
        
        
        
        
        
      
    
      
        

    Task Application

    This is an AngularJS application that demonstrates a generalized recursive templating directive. Use it to quickly produce recursive structures in templates.

    The recursive directive was developed in order to avoid the need for recursive structures to be given their own templates and be explicitly self-referential, as would be required with ngInclude. Owing to its high priority, it should also be possible to use it for recursive directives (directives that have templates which include the directive) that would otherwise send the compiler into infinite recursion.

    The directive can be used alongside ng-if and ng-repeat to create recursive structures without the need for additional container elements.

    Since the directive does not request a scope (either isolated or not) it should not impair reasoning about scope visibility, which continues to behave as the template suggests.

    Try playing around with the demonstration, below, where the input at the top provides a way to modify a scope attribute. Observe how the value is visible at all levels.

    The collapse directive is included to further demonstrate that the recursion can co-exist with other transclusions (not just ngIf, et al) and that sibling directives are included on the recursive due to the recursion using whole 'element' transclusion.

    Tasks

    • {{task.name}}

      Volunteers
      • {{who}}
      • {{you}} (you)

提交回复
热议问题