programing

각도 방향의 재귀

easyjava 2023. 2. 28. 23:52
반응형

각도 방향의 재귀

널리 사용되는 재귀 각도 지시 Q&A가 몇 가지 있는데, 모두 다음 솔루션 중 하나로 귀결됩니다.

  • HTML の 컴컴컴컴컴 컴컴컴컴 。
  • 디렉티브는 전혀 사용하지 않고 자신을 참조하는 <script>템플릿을 사용합니다.

번째는 수동 컴파일 프로세스를 포괄적으로 관리하지 않으면 이전에 컴파일된 코드를 제거할 수 없다는 문제가 있습니다. 번째 방법에는...의 문제가 있다.강력한 기능을 놓치는 것은 아니지만, 더 시급한 것은 명령과 동일한 방식으로 매개 변수를 지정할 수 없다는 것입니다. 단순히 새로운 컨트롤러 인스턴스에 바인딩되어 있기 때문입니다.

는 을 하고 .angular.bootstrap ★★★★★★★★★★★★★★★★★」@compile()해야 하는 합니다.

런타임 상태를 반영하기 위해 요소 추가/제거를 관리하는 매개 변수화된 재귀 패턴을 갖는 좋은 방법이 있습니까?즉, 노드 추가/삭제 버튼과 노드의 자노드로 값이 전달되는 입력 필드가 있는 트리입니다.두 번째 접근법과 체인 스코프의 조합(단, 이 방법은 전혀 모릅니다)

@dnc253에서 언급한 스레드에 기술된 솔루션에서 영감을 얻어 재귀 기능을 서비스로 추상화했습니다.

module.factory('RecursionHelper', ['$compile', function($compile){
    return {
        /**
         * Manually compiles the element, fixing the recursion loop.
         * @param element
         * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
         * @returns An object containing the linking functions.
         */
        compile: function(element, link){
            // Normalize the link parameter
            if(angular.isFunction(link)){
                link = { post: link };
            }

            // Break the recursion loop by removing the contents
            var contents = element.contents().remove();
            var compiledContents;
            return {
                pre: (link && link.pre) ? link.pre : null,
                /**
                 * Compiles and re-adds the contents
                 */
                post: function(scope, element){
                    // Compile the contents
                    if(!compiledContents){
                        compiledContents = $compile(contents);
                    }
                    // Re-add the compiled contents to the element
                    compiledContents(scope, function(clone){
                        element.append(clone);
                    });

                    // Call the post-linking function, if any
                    if(link && link.post){
                        link.post.apply(null, arguments);
                    }
                }
            };
        }
    };
}]);

이는 다음과 같이 사용됩니다.

module.directive("tree", ["RecursionHelper", function(RecursionHelper) {
    return {
        restrict: "E",
        scope: {family: '='},
        template: 
            '<p>{{ family.name }}</p>'+
            '<ul>' + 
                '<li ng-repeat="child in family.children">' + 
                    '<tree family="child"></tree>' +
                '</li>' +
            '</ul>',
        compile: function(element) {
            // Use the compile function from the RecursionHelper,
            // And return the linking function(s) which it returns
            return RecursionHelper.compile(element);
        }
    };
}]);

데모는 이 Plunker를 참조하십시오.이 솔루션이 가장 마음에 드는 이유는 다음과 같습니다.

  1. html을 덜 깔끔하게 만드는 특별한 지시문은 필요하지 않습니다.
  2. 재귀 논리는 재귀로 추상화됩니다.도우미 서비스, 그러니 당신은 당신의 지시를 깨끗하게 유지하세요.

업데이트: Angular 1.5.x 에서는 트릭은 불필요하지만 templateUrl 에서는 동작하지 않습니다.

요소를 수동으로 추가하고 컴파일하는 것은 분명 완벽한 접근법입니다.ng-repeat을 사용하는 경우 요소를 수동으로 삭제할 필요가 없습니다.

데모: http://jsfiddle.net/KNM4q/113/

.directive('tree', function ($compile) {
return {
    restrict: 'E',
    terminal: true,
    scope: { val: '=', parentData:'=' },
    link: function (scope, element, attrs) {
        var template = '<span>{{val.text}}</span>';
        template += '<button ng-click="deleteMe()" ng-show="val.text">delete</button>';

        if (angular.isArray(scope.val.items)) {
            template += '<ul class="indent"><li ng-repeat="item in val.items"><tree val="item" parent-data="val.items"></tree></li></ul>';
        }
        scope.deleteMe = function(index) {
            if(scope.parentData) {
                var itemIndex = scope.parentData.indexOf(scope.val);
                scope.parentData.splice(itemIndex,1);
            }
            scope.val = {};
        };
        var newElement = angular.element(template);
        $compile(newElement)(scope);
        element.replaceWith(newElement);
    }
}
});

이 솔루션이 링크된 예나 동일한 기본 개념 중 하나에 있는지 확실하지 않지만, 재귀 지시어가 필요했고, 훌륭하고 쉬운 해결책을 찾았습니다.

module.directive("recursive", function($compile) {
    return {
        restrict: "EACM",
        priority: 100000,
        compile: function(tElement, tAttr) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents);
                }
                iElement.append(
                    compiledContents(scope, 
                                     function(clone) {
                                         return clone; }));
            };
        }
    };
});

module.directive("tree", function() {
    return {
        scope: {tree: '='},
        template: '<p>{{ tree.text }}</p><ul><li ng-repeat="child in tree.children"><recursive><span tree="child"></span></recursive></li></ul>',
        compile: function() {
            return  function() {
            }
        }
    };
});​

해서 '만들다'를 만들어야 합니다.recursive재귀 콜을 발신하는 요소를 감싸도록 지시합니다.

Angular 1.5.x부터는 더 이상의 트릭이 필요하지 않으므로 다음을 수행할 수 있습니다.더 이상 지저분한 일을 할 필요가 없습니다!

이 발견은 재귀 디렉티브를 위한 보다 나은/깨끗한 솔루션을 모색한 결과입니다.https://jsfiddle.net/cattails27/5j5au76c/ 에서 찾을 수 있습니다.1.3.x까지 지원됩니다.

angular.element(document).ready(function() {
  angular.module('mainApp', [])
    .controller('mainCtrl', mainCtrl)
    .directive('recurv', recurveDirective);

  angular.bootstrap(document, ['mainApp']);

  function recurveDirective() {
    return {
      template: '<ul><li ng-repeat="t in tree">{{t.sub}}<recurv tree="t.children"></recurv></li></ul>',
      scope: {
        tree: '='
      },
    }
  }

});

  function mainCtrl() {
    this.tree = [{
      title: '1',
      sub: 'coffee',
      children: [{
        title: '2.1',
        sub: 'mocha'
      }, {
        title: '2.2',
        sub: 'latte',
        children: [{
          title: '2.2.1',
          sub: 'iced latte'
        }]
      }, {
        title: '2.3',
        sub: 'expresso'
      }, ]
    }, {
      title: '2',
      sub: 'milk'
    }, {
      title: '3',
      sub: 'tea',
      children: [{
        title: '3.1',
        sub: 'green tea',
        children: [{
          title: '3.1.1',
          sub: 'green coffee',
          children: [{
            title: '3.1.1.1',
            sub: 'green milk',
            children: [{
              title: '3.1.1.1.1',
              sub: 'black tea'
            }]
          }]
        }]
      }]
    }];
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<div>
  <div ng-controller="mainCtrl as vm">
    <recurv tree="vm.tree"></recurv>
  </div>
</div>

잠시 몇 가지 회피책을 사용한 후, 이 문제에 대해 반복해서 언급하고 있습니다.

서비스 솔루션은 서비스를 삽입할 수 있는 디렉티브에서는 동작하지만 익명 템플릿의 fragment에서는 동작하지 않기 때문에 서비스 솔루션에 만족하지 않습니다.

마찬가지로 명령어로 DOM 조작을 함으로써 특정 템플릿 구조에 의존하는 솔루션은 너무 구체적이고 취약합니다.

저는 재귀가 그 자체의 지침으로 캡슐화되어 다른 지시사항을 최소화하고 익명으로 사용할 수 있는 범용 솔루션을 가지고 있습니다.

아래는 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;
}
<!DOCTYPE html>
<html>

  <head>
    <link href="collapse.css" rel="stylesheet" />
    <link href="style.css" rel="stylesheet" />
    <script data-require="angular.js@1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js" data-semver="2.1.1" data-require="jquery@*"></script>
    <script src="script.js"></script>
    <script>
      function AppController($scope) {
        $scope.toggleCollapsed = function ($event) {
          $event.preventDefault();
          $event.stopPropagation();
          this.collapsed = !this.collapsed;
        }
        
        $scope.task = {
          name: 'All tasks',
          assignees: ['Citizens'],
          children: [
            {
              name: 'Gardening',
              assignees: ['Gardeners', 'Horticulture Students'],
              children: [
                {
                  name: 'Pull weeds',
                  assignees: ['Weeding Sub-committee']
                }
              ],
            },
            {
              name: 'Cleaning',
              assignees: ['Cleaners', 'Guests']
            }
          ]
        }
      }
      
      angular.module('app', ['h'])
      .controller('AppController', AppController)
    </script>
  </head>

  <body ng-app="app" ng-controller="AppController">
    <h1>Task Application</h1>
    
    <p>This is an AngularJS application that demonstrates a generalized
    recursive templating directive. Use it to quickly produce recursive
    structures in templates.</p>
    
    <p>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.</p>
    
    <p>The directive can be used alongside ng-if
    and ng-repeat to create recursive structures without the need for
    additional container elements.</p>
    
    <p>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.</p>
    
    <p>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.</p>
    
    <p>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.</p>
    
    <label for="volunteer">Citizen name:</label>
    <input id="volunteer" ng-model="you" placeholder="your name">
    <h2>Tasks</h2>
    <ul class="task-list">
      <li class="task" h-collapse h-recursive>
        <h4>{{task.name}}</h4>
        <h5>Volunteers</h5>
        <ul>
          <li ng-repeat="who in task.assignees">{{who}}</li>
          <li>{{you}} (you)</li>
        </ul>
        <ul class="task-list">
          <li h-recursive ng-repeat="task in task.children"></li>
        </ul>
      <li>
    </ul>
    
    <script type="text/ng-template" id="collapse.html">
      <div class="h-collapse-wrapper">
        <a class="h-collapse-expander" href="#" ng-click="collapse = !collapse"></a>
        <div class="h-collapse-item" ng-transclude></div>
      </div>
    </script>
  </body>

</html>

이제 Angular 2.0이 프리뷰에 나왔으니 Angular 2.0을 믹스에 추가해도 괜찮을 것 같습니다.적어도 나중에 사람들에게 도움이 될 것이다.

주요 개념은 자기 참조를 사용하여 재귀 템플릿을 구축하는 것입니다.

<ul>
    <li *for="#dir of directories">

        <span><input type="checkbox" [checked]="dir.checked" (click)="dir.check()"    /></span> 
        <span (click)="dir.toggle()">{{ dir.name }}</span>

        <div *if="dir.expanded">
            <ul *for="#file of dir.files">
                {{file}}
            </ul>
            <tree-view [directories]="dir.directories"></tree-view>
        </div>
    </li>
</ul>

그런 다음 트리 개체를 템플릿에 바인딩하고 재귀가 나머지를 처리하는 것을 확인합니다.다음은 완전한 예를 제시하겠습니다.http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0

여기에는 지시문이 전혀 필요 없는 매우 간단한 회피책이 있습니다.

그런 의미에서 디렉티브가 필요하다고 가정할 경우 원래 문제의 해결책이 아닐 수도 있지만 GUI의 파라미터화된 하위 구조를 가진 재귀적인 GUI 구조를 원할 경우에는 해결책이 될 수 있습니다.그게 네가 원하는 거겠지

이 솔루션은 ng-controller, ng-init 및 ng-include 사용만을 기반으로 합니다.다음과 같이 합니다.컨트롤러 이름이 "MyController"이고 템플릿이 myTemplate.html에 있으며 컨트롤러에 인수 A, B 및 C를 사용하는 init이라는 초기화 함수가 있다고 가정하면 컨트롤러의 파라미터를 설정할 수 있습니다.그러면 솔루션은 다음과 같습니다.

myTemplate.htlm:

<div> 
    <div>Hello</div>
    <div ng-if="some-condition" ng-controller="Controller" ng-init="init(A, B, C)">
       <div ng-include="'myTemplate.html'"></div>
    </div>
</div>

이런 구조는 바닐라 앵글에서 당신이 원하는 대로 재귀적으로 만들어질 수 있다는 것을 우연히 알게 되었습니다.이 디자인 패턴만 따르면 고급 컴파일 팅커링 등 없이 재귀 UI 구조를 사용할 수 있습니다.

컨트롤러 내부:

$scope.init = function(A, B, C) {
   // Do something with A, B, C
   $scope.D = A + B; // D can be passed on to other controllers in myTemplate.html
} 

내가 볼 수 있는 유일한 단점은 당신이 참아야 하는 투박한 구문이다.

angular-recursion-disclusion-disclushttp://https://github.com/knyga/angular-recursion-injector

컨디셔닝과 함께 무제한 깊이 중첩을 수행할 수 있습니다.필요한 경우에만 재컴파일하고 올바른 요소만 컴파일합니다.암호에 마법은 없다.

<div class="node">
  <span>{{name}}</span>

  <node--recursion recursion-if="subNode" ng-model="subNode"></node--recursion>
</div>

다른 솔루션보다 빠르고 간단하게 동작할 수 있는 것 중 하나는 "--재귀" 접미사입니다.

재귀에 대한 기본 지시사항을 작성하게 되었습니다.

IMO 이 솔루션은 여기서 발견된 솔루션보다 훨씬 기본적이고 유연하기 때문에 UL/LI 구조 등을 사용할 필요가 없습니다.하지만 분명히 사용하는 것은 말이 되지만, 지시는 이 사실을 모르고 있습니다.

Super simple의 예는 다음과 같습니다.

<ul dx-start-with="rootNode">
  <li ng-repeat="node in $dxPrior.nodes">
    {{ node.name }}
    <ul dx-connect="node"/>
  </li>
</ul>

'syslog-start-with'와 'syslog-connect'의 구현은 https://github.com/dotJEM/angular-tree에서 확인할 수 있습니다.

즉, 8개의 다른 레이아웃이 필요한 경우 8개의 디렉티브를 작성할 필요가 없습니다.

그 위에 노드를 추가하거나 삭제할 수 있는 트리뷰를 작성하는 것은 매우 간단합니다.예: http://codepen.io/anon/pen/BjXGbY?editors=1010

angular
  .module('demo', ['dotjem.angular.tree'])
  .controller('AppController', function($window) {

this.rootNode = {
  name: 'root node',
  children: [{
    name: 'child'
  }]
};

this.addNode = function(parent) {
  var name = $window.prompt("Node name: ", "node name here");
  parent.children = parent.children || [];
  parent.children.push({
    name: name
  });
}

this.removeNode = function(parent, child) {
  var index = parent.children.indexOf(child);
  if (index > -1) {
    parent.children.splice(index, 1);
  }
}

  });
<div ng-app="demo" ng-controller="AppController as app">
  HELLO TREE
  <ul dx-start-with="app.rootNode">
<li><button ng-click="app.addNode($dxPrior)">Add</button></li>
<li ng-repeat="node in $dxPrior.children">
  {{ node.name }} 
  <button ng-click="app.removeNode($dxPrior, node)">Remove</button>
  <ul dx-connect="node" />
</li>
  </ul>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
  <script src="https://rawgit.com/dotJEM/angular-tree-bower/master/dotjem-angular-tree.min.js"></script>

</div>

이 시점부터 컨트롤러와 템플릿은 필요에 따라 독자적인 지시로 포장할 수 있습니다.

언급URL : https://stackoverflow.com/questions/14430655/recursion-in-angular-directives

반응형