source

Angular로 트리뷰를 만들 수 있습니까?

nicesource 2023. 3. 10. 22:06
반응형

Angular로 트리뷰를 만들 수 있습니까?

웹 앱에서 트리 구조로 데이터를 표시하려고 합니다.나는 Angular를 이 작업에 사용하고 싶었다.

ng-repeat으로 노드 목록을 반복할 수 있을 것 같습니다만, 특정 노드의 깊이가 커지면 어떻게 네스트를 할 수 있을까요?

아래 코드를 시도했지만 HTML의 자동 이스케이핑으로 인해 동작하지 않습니다.게다가 끝 ul 태그가 잘못된 곳에 있어요.

내가 이 문제에 대해 완전히 잘못된 방향으로 가고 있는 것이 확실하다.

좋은 생각 있어요?

이 바이올린 좀 봐

원본 : http://jsfiddle.net/brendanowen/uXbn6/8/

갱신일 : http://jsfiddle.net/animaxf/uXbn6/4779/

더 잘 알 수예요.tree like structure htmlhtml 있습니다.

부트스트랩 CSS를 사용하는 경우...

Angular의 간단한 재사용 가능한 트리 컨트롤(디렉티브)을 작성했습니다.부트스트랩 "nav" 목록에 기반한 JS.들여쓰기, 아이콘, 애니메이션을 추가했습니다.설정에 HTML 어트리뷰트가 사용됩니다.

재귀는 사용하지 않습니다.

나는 그것을 angular-bootstrap-nav-tree라고 불렀다.)

여기 가 있고, 출처는 여기입니다.

이와 같은 것을 만들 때 가장 좋은 해결책은 재귀적 지시입니다.하지만 그런 지시를 내리면 Angular가JS는 끝없는 루프가 됩니다.

이를 위한 해결책은 디렉티브가 컴파일 이벤트 중에 요소를 삭제하고 수동으로 컴파일하여 링크이벤트에 추가하는 것입니다.

저는 이 스레드에서 이 기능을 발견하고 이 기능을 서비스로 추상화했습니다.

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", 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) {
            return RecursionHelper.compile(element);
        }
    };
});

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

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

업데이트: 커스텀 링크 기능에 대한 지원이 추가되었습니다.

앵귤나무는 나에게 좋은 것 같다.

다음은 재귀 지시어를 사용한 예입니다.http://jsfiddle.net/n8dPm/ Taken from https://groups.google.com/forum/ # ! topic / angular / vswXTes _ FtM

module.directive("tree", function($compile) {
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(tElement, tAttr) {
        var contents = tElement.contents().remove();
        var compiledContents;
        return function(scope, iElement, iAttr) {
            if(!compiledContents) {
                compiledContents = $compile(contents);
            }
            compiledContents(scope, function(clone, scope) {
                     iElement.append(clone); 
            });
        };
    }
};
});

이게 좀 더 완벽한 것 같아요.https://github.com/dump247/angular.tree

원래의 소스를 기반으로 한 다른 예에서는 샘플트리 구조가 이미 준비되어 있고(IMO의 구조를 쉽게 확인할 수 있음), 트리를 검색하는 필터가 있습니다.

JSFiddle

정말 많은 훌륭한 해결책들이 있지만, 이런저런 면에서 너무 복잡하다고 느껴집니다.

@Mark Lagendijk의 awnser의 심플함을 재현할 수 있는 것을 만들고 싶었습니다만, 디렉티브에 템플릿을 정의하지 않고, 대신에 「사용자」가 HTML로 템플릿을 작성할 수 있도록 하고 싶다고 생각하고 있습니다.

https://github.com/stackfull/angular-tree-repeat 등의 아이디어로...저는 https://github.com/dotJEM/angular-tree 프로젝트를 만들었습니다.

이를 통해 다음과 같은 트리를 구축할 수 있습니다.

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

서로 다른 구조를 가진 나무를 위해 여러 개의 지시문을 작성하는 것보다 더 깔끔합니다.본질적으로 위의 것을 트리라고 부르는 것은 약간 잘못된 것입니다.그것은 @ganaraj의 "재귀 템플릿"의 awnser에서 훨씬 더 많은 것을 추출하지만, 트리가 필요한 곳에 템플릿을 정의할 수 있습니다.

(스크립트 태그 기반 템플릿을 사용하면 가능하지만 실제 트리 노드 바로 바깥에 배치해야 하며, 여전히 약간 육감적으로 느껴집니다.)

다른 선택을 위해 이곳을 떠난 건...

Angular-Ui-Tree를 사용한 Angular-Tree-DnD 샘플로 시험할 수 있지만 테이블, 그리드, 목록과의 호환성을 편집했습니다.

  • 드래그 앤 드롭 기능
  • 목록에 대한 확장 함수 지시문(next, prev, getChildren)
  • 데이터를 필터링 합니다.
  • OrderBy (버전

@ganaraj의 답변과 @dnc253의 답변을 바탕으로 선택, 추가, 삭제 및 편집 기능을 가진 트리 구조를 간단하게 지시했습니다.

Jsfiddle : http://jsfiddle.net/yoshiokatsuneo/9dzsms7y/

HTML:

<script type="text/ng-template" id="tree_item_renderer.html">
    <div class="node"  ng-class="{selected: data.selected}" ng-click="select(data)">
        <span ng-click="data.hide=!data.hide" style="display:inline-block; width:10px;">
            <span ng-show="data.hide && data.nodes.length > 0" class="fa fa-caret-right">+</span>
            <span ng-show="!data.hide && data.nodes.length > 0" class="fa fa-caret-down">-</span>
        </span>
        <span ng-show="!data.editting" ng-dblclick="edit($event)" >{{data.name}}</span>
        <span ng-show="data.editting"><input ng-model="data.name" ng-blur="unedit()" ng-focus="f()"></input></span>
        <button ng-click="add(data)">Add node</button>
        <button ng-click="delete(data)" ng-show="data.parent">Delete node</button>
    </div>
    <ul ng-show="!data.hide" style="list-style-type: none; padding-left: 15px">
        <li ng-repeat="data in data.nodes">
            <recursive><sub-tree data="data"></sub-tree></recursive>
        </li>
    </ul>
</script>
<ul ng-app="Application" style="list-style-type: none; padding-left: 0">
    <tree data='{name: "Node", nodes: [],show:true}'></tree>
</ul>

JavaScript:

angular.module("myApp",[]);

/* https://stackoverflow.com/a/14657310/1309218 */
angular.module("myApp").
directive("recursive", function($compile) {
    return {
        restrict: "EACM",
        require: '^tree',
        priority: 100000,

        compile: function(tElement, tAttr) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents);
                }
                compiledContents(scope, 
                                     function(clone) {
                         iElement.append(clone);
                                         });
            };
        }
    };
});

angular.module("myApp").
directive("subTree", function($timeout) {
    return {
        restrict: 'EA',
        require: '^tree',
        templateUrl: 'tree_item_renderer.html',
        scope: {
            data: '=',
        },
        link: function(scope, element, attrs, treeCtrl) {
            scope.select = function(){
                treeCtrl.select(scope.data);
            };
            scope.delete = function() {
                scope.data.parent.nodes.splice(scope.data.parent.nodes.indexOf(scope.data), 1);
            };
            scope.add = function() {
                var post = scope.data.nodes.length + 1;
                var newName = scope.data.name + '-' + post;
                scope.data.nodes.push({name: newName,nodes: [],show:true, parent: scope.data});
            };
            scope.edit = function(event){
                scope.data.editting = true;
                $timeout(function(){event.target.parentNode.querySelector('input').focus();});
            };
            scope.unedit = function(){
                scope.data.editting = false;
            };

        }
    };
});


angular.module("myApp").
directive("tree", function(){
    return {
        restrict: 'EA',
        template: '<sub-tree data="data" root="data"></sub-tree>',
        controller: function($scope){
            this.select = function(data){
                if($scope.selected){
                    $scope.selected.selected = false;
                }
                data.selected = true;
                $scope.selected = data;
            };
        },
        scope: {
            data: '=',
        }
    }
});

네, 확실히 가능합니다.여기서의 질문은 아마도 Angular 1.x를 가정하고 있지만, 향후 참조를 위해 Angular 2의 예를 포함하고 있습니다.

개념적으로는 재귀 템플릿을 작성하기만 하면 됩니다.

<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>

그런 다음 트리 개체를 템플릿에 바인딩하고 Angular가 마법의 기능을 수행하도록 합니다.이 개념은 Angular 1.x에도 적용할 수 있습니다.

다음은 완전한 예를 제시하겠습니다.http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0

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>

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

트리 구조가 클 경우 Angular(최대 1.4.x)는 재귀 템플릿 렌더링 속도가 매우 느려집니다.이 제안들을 여러 번 시도해 본 후, 저는 간단한 HTML 문자열을 만들고ng-bind-html표시할 수 있습니다.물론 각도 피쳐를 사용하는 방법은 아닙니다.

베어본 재귀 함수는 다음과 같습니다(최소 HTML 사용).

function menu_tree(menu, prefix) {
    var html = '<div>' + prefix + menu.menu_name + ' - ' + menu.menu_desc + '</div>\n';
    if (!menu.items) return html;
    prefix += menu.menu_name + '/';
    for (var i=0; i<menu.items.length; ++i) {
        var item = menu.items[i];
        html += menu_tree(item, prefix);
    }
    return html;
}
// Generate the tree view and tell Angular to trust this HTML
$scope.html_menu = $sce.trustAsHtml(menu_tree(menu, ''));

템플릿에서는 다음 한 줄만 필요합니다.

<div ng-bind-html="html_menu"></div>

이렇게 하면 Angular의 모든 데이터 바인딩이 무시되고 HTML이 재귀 템플릿 메서드의 짧은 시간 내에 표시됩니다.

다음과 같은 메뉴 구조(Linux 파일 시스템의 일부 파일 트리):

menu = {menu_name: '', menu_desc: 'root', items: [
            {menu_name: 'bin', menu_desc: 'Essential command binaries', items: [
                {menu_name: 'arch', menu_desc: 'print machine architecture'},
                {menu_name: 'bash', menu_desc: 'GNU Bourne-Again SHell'},
                {menu_name: 'cat', menu_desc: 'concatenate and print files'},
                {menu_name: 'date', menu_desc: 'display or set date and time'},
                {menu_name: '...', menu_desc: 'other files'}
            ]},
            {menu_name: 'boot', menu_desc: 'Static files of the boot loader'},
            {menu_name: 'dev', menu_desc: 'Device files'},
            {menu_name: 'etc', menu_desc: 'Host-specific system configuration'},
            {menu_name: 'lib', menu_desc: 'Essential shared libraries and kernel modules'},
            {menu_name: 'media', menu_desc: 'Mount point for removable media'},
            {menu_name: 'mnt', menu_desc: 'Mount point for mounting a filesystem temporarily'},
            {menu_name: 'opt', menu_desc: 'Add-on application software packages'},
            {menu_name: 'sbin', menu_desc: 'Essential system binaries'},
            {menu_name: 'srv', menu_desc: 'Data for services provided by this system'},
            {menu_name: 'tmp', menu_desc: 'Temporary files'},
            {menu_name: 'usr', menu_desc: 'Secondary hierarchy', items: [
                {menu_name: 'bin', menu_desc: 'user utilities and applications'},
                {menu_name: 'include', menu_desc: ''},
                {menu_name: 'local', menu_desc: '', items: [
                    {menu_name: 'bin', menu_desc: 'local user binaries'},
                    {menu_name: 'games', menu_desc: 'local user games'}
                ]},
                {menu_name: 'sbin', menu_desc: ''},
                {menu_name: 'share', menu_desc: ''},
                {menu_name: '...', menu_desc: 'other files'}
            ]},
            {menu_name: 'var', menu_desc: 'Variable data'}
        ]
       }

출력은 다음과 같습니다.

- root
/bin - Essential command binaries
/bin/arch - print machine architecture
/bin/bash - GNU Bourne-Again SHell
/bin/cat - concatenate and print files
/bin/date - display or set date and time
/bin/... - other files
/boot - Static files of the boot loader
/dev - Device files
/etc - Host-specific system configuration
/lib - Essential shared libraries and kernel modules
/media - Mount point for removable media
/mnt - Mount point for mounting a filesystem temporarily
/opt - Add-on application software packages
/sbin - Essential system binaries
/srv - Data for services provided by this system
/tmp - Temporary files
/usr - Secondary hierarchy
/usr/bin - user utilities and applications
/usr/include -
/usr/local -
/usr/local/bin - local user binaries
/usr/local/games - local user games
/usr/sbin -
/usr/share -
/usr/... - other files
/var - Variable data

복잡하지 않아요.

<div ng-app="Application" ng-controller="TreeController">
    <table>
        <thead>
            <tr>
                <th>col 1</th>
                <th>col 2</th>
                <th>col 3</th>
            </tr>
        </thead>
        <tbody ng-repeat="item in tree">
            <tr>
                <td>{{item.id}}</td>
                <td>{{item.fname}}</td>
                <td>{{item.lname}}</td>
            </tr>
            <tr ng-repeat="children in item.child">
                <td style="padding-left:15px;">{{children.id}}</td>
                <td>{{children.fname}}</td>
            </tr>
        </tbody>
     </table>
</div>

컨트롤러 코드:

angular.module("myApp", []).
controller("TreeController", ['$scope', function ($scope) {
    $scope.tree = [{
        id: 1,
        fname: "tree",
        child: [{
            id: 1,
            fname: "example"
        }],
        lname: "grid"
    }];


}]);

언급URL : https://stackoverflow.com/questions/11854514/is-it-possible-to-make-a-tree-view-with-angular

반응형