เป็นไปได้ไหมที่จะทำให้ Tree View เป็น Angular


177

ฉันต้องการแสดงข้อมูลในโครงสร้างแบบต้นไม้ในเว็บแอป ฉันหวังว่าจะใช้ Angular สำหรับงานนี้

ดูเหมือนว่า ng-repeat จะอนุญาตให้ฉันวนซ้ำรายการของโหนด แต่ฉันจะทำรังอย่างไรเมื่อความลึกของโหนดที่กำหนดเพิ่มขึ้น?

ฉันลองใช้รหัสต่อไปนี้แต่การหลีกเลี่ยง HTML โดยอัตโนมัติทำให้ไม่สามารถทำงานได้ นอกจากนี้แท็ก ul ปลายอยู่ผิดตำแหน่ง

ฉันค่อนข้างแน่ใจว่าฉันจะเกี่ยวกับปัญหานี้ในทางที่ผิดทั้งหมด

ความคิดใด ๆ


ฉันเพิ่งตอบคำถามนี้ด้วยวิธีทั่วไปในคำถามอื่น: stackoverflow.com/questions/14430655/…
tilgovi

คำตอบ:


231

ดูซอนี้

ต้นฉบับ: http://jsfiddle.net/brendanowen/uXbn6/8/

อัปเดต: http://jsfiddle.net/animaxf/uXbn6/4779/

นี่ควรให้ความคิดที่ดีเกี่ยวกับวิธีการแสดงtree like structureมุมที่ใช้ มันเป็นชนิดของการใช้การเรียกซ้ำใน html!


94
ทำไมไม่ระบุแหล่งที่มาของคุณ ? คุณเขียนโพสต์ในหัวข้อนั้นและตอนนี้คุณโพสต์ url ที่นี่ด้วยชื่อของคุณเองหรือไม่
Janus Troelsen

5
นี่คือรุ่นที่เหมือนกัน (ฉันคิดว่า) ยกเว้นว่ามันจะโหลดเร็วกว่ามาก (สำหรับฉันอย่างน้อย) เนื่องจากไม่มี Twitter Bootstrap ที่อยู่ในส่วนของ CSS jsfiddle.net/brendanowen/uXbn6/8
KajMagnus

10
เพื่อนคุณควรระบุแหล่งที่มาของคุณ
Ajax3.14

46
ฉันรู้สึกเบื่อหน่ายจริง ๆ กับคนที่แสดงความคิดเห็นเกี่ยวกับเรื่องนี้อย่างต่อเนื่องว่า URL มีชื่อของฉันอยู่ในนั้น (และนั่นก็คือการลอกเลียนแบบ!) น่าเสียดายที่ jsfiddle ทำงานอย่างไร หากคุณแยกบางอย่างขณะที่คุณเข้าสู่ระบบจะรักษาชื่อผู้ใช้ของคุณ ต้องบอกว่าตอนนี้ฉันได้เชื่อมโยงกับ URL เดิมแล้ว ลงคำตอบถ้าผิด - คำตอบนั้นถูกต้องในสถานการณ์นี้โดยมีสิ่งหนึ่งที่ URL สำรองที่ฉันมีดูเหมือนจะมีชื่อของฉันอยู่
ganaraj

5
ฉันเพิ่งเพิ่มปุ่มยุบและขยายในเวอร์ชันของคุณ: jsfiddle.net/uXbn6/639
jbaylina

77

หากคุณใช้ Bootstrap CSS ...

ฉันได้สร้างตัวควบคุมทรีที่ใช้งานได้ง่าย (คำสั่ง) สำหรับ AngularJS ตามรายการ Bootstrap "nav" ฉันได้เพิ่มการเยื้องไอคอนและภาพเคลื่อนไหวพิเศษ คุณลักษณะ HTML ใช้สำหรับการกำหนดค่า

มันไม่ได้ใช้การเรียกซ้ำ

ฉันเรียกมันว่าangular-bootstrap-nav-tree (ชื่อลวงคุณไม่คิดเหรอ?)

มีตัวอย่างคือที่นี่และแหล่งที่มาที่นี่


1
มันสวยงาม แต่ถูกเตือนว่ามันไม่สามารถใช้งานได้ในสาขา Angular 1.0.x
Danita

3
ใช่มันใช้สิ่งเคลื่อนไหวใหม่ ... ต้องใช้ Angular 1.1.5 (ฉันคิดว่าไง)
Nick Perkins

3
UPDATE: ตอนนี้ใช้ได้กับ Angular 1.1.5 หรือ Angular 1.2.0 และยังสามารถใช้ได้กับ Bootsrap 2 หรือ Bootstrap 3
Nick Perkins

1
FYI เท่านั้นหากใช้ Bower ตอนนี้ Nick ได้ทำให้สิ่งนี้พร้อมสำหรับการติดตั้งง่าย - "ค้นหา bower angular-bootstrap-nav-tree" และ "bower ติดตั้ง angular-bootstrap-nav-tree - ประหยัด" และคุณเสร็จสิ้นแล้ว
arcseldon

2
@Nick Perkins - คุณช่วยอธิบายได้ว่าทำไม angular-bootstrap-nav-tree ของคุณไม่มี API สำหรับการลบ Branch / Node อย่างน้อยจากการตรวจสอบอย่างรวดเร็วของแหล่งที่มาและการตรวจสอบการทดสอบ / ตัวอย่างของคุณดูเหมือนจะไม่เป็นตัวเลือกนั้น นี่คือการละเว้นที่สำคัญอย่างแน่นอน?
arcseldon

35

เมื่อทำอะไรแบบนี้ทางออกที่ดีที่สุดคือคำสั่งแบบเรียกซ้ำ อย่างไรก็ตามเมื่อคุณทำตามคำสั่งดังกล่าวคุณจะพบว่า AngularJS เข้าสู่วงวนไม่รู้จบ

วิธีการแก้ปัญหานี้คือการให้คำสั่งลบองค์ประกอบในระหว่างเหตุการณ์รวบรวมและรวบรวมด้วยตนเองและเพิ่มในกิจกรรมลิงค์

ผมพบเกี่ยวกับเรื่องนี้ในหัวข้อนี้และแยกการทำงานนี้เข้าสู่บริการ

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. ตรรกะการเรียกซ้ำถูกแยกออกไปยังบริการ RecursionHelper ดังนั้นคุณจึงรักษาคำสั่งของคุณให้สะอาด

อัปเดต: เพิ่มการรองรับฟังก์ชั่นการเชื่อมโยงที่กำหนดเอง


1
สิ่งนี้ดูเหมือนจะเรียบร้อยและมีประสิทธิภาพความคิดใด ๆ ที่ว่าทำไมนี่ไม่ใช่พฤติกรรมเริ่มต้นใน angularjs
พอล

เมื่อใช้ "คอมไพล์" เช่นนี้จะเพิ่มคุณสมบัติเพิ่มเติมในขอบเขตได้อย่างไร ดูเหมือนว่าฟังก์ชั่น "ลิงก์" จะไม่สามารถใช้งานได้อีกต่อไปเมื่อมี "คอมไพล์" ...
Brian Kent

1
@ bkent314 ฉันได้เพิ่มการสนับสนุนสำหรับสิ่งนี้ ตอนนี้ยอมรับการเชื่อมโยงฟังก์ชั่นในลักษณะเดียวกับที่คอมไพล์สามารถส่งคืน ฉันยังสร้างโครงการ Github สำหรับบริการ
Mark Lagendijk

@ MarkLagendijk มากเนียนมาก! คุณสมควรได้รับ upvotes มากมายสำหรับการสรุปการเรียกซ้ำออกจากคำสั่ง คำสั่งทั้งหมดที่ฉันเห็นมีความซับซ้อนอย่างสิ้นหวังกับตรรกะนั้นปะปนกันมีวิธีที่จะทำให้ RecursionHelper ของคุณทำงานกับการสลับสับเปลี่ยนได้หรือไม่?
acjay

ฉันขอแนะนำให้คุณทิ้งข้อมูลลงในโซลูชันประเภทนี้ - ใช่เกือบทุกคนใช้แผนผังที่มีคำสั่งแบบเรียกซ้ำมันง่าย แต่มันช้ามากเมื่อทำซ้ำ $ digest ของเมื่อคุณไปถึงหลายร้อยโหนดสิ่งนี้จะไม่ทำงาน
Artemiy


15

นี่คือตัวอย่างการใช้คำสั่งแบบเรียกซ้ำ: http://jsfiddle.net/n8dPm/ ถ่ายจาก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); 
            });
        };
    }
};
});

ฉันกำลังทดลองกับสิ่งนี้และฉันต้องการที่จะใช้การแปลงเพศด้วยคุณคิดว่ามันเป็นไปได้หรือไม่?
L.Trabacchin


5

อีกตัวอย่างหนึ่งอ้างอิงจากแหล่งต้นฉบับโดยมีโครงสร้างตัวอย่างต้นไม้อยู่แล้ว (ง่ายกว่าที่จะดูว่ามันทำงานอย่างไร IMO) และตัวกรองเพื่อค้นหาต้นไม้:

JSFiddle


4

วิธีแก้ปัญหาที่ยอดเยี่ยมมากมาย แต่ฉันรู้สึกว่าพวกเขาทั้งหมดไม่ทางใดก็ทางหนึ่งที่ซับซ้อนเกินไป

ฉันต้องการสร้างบางสิ่งที่สร้างความเรียบง่ายของ awnser ของ @Mark Lagendijk แต่ไม่มีการกำหนดเทมเพลตในคำสั่ง แต่จะปล่อยให้ "ผู้ใช้" สร้างเทมเพลตใน 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>

ซึ่งสำหรับฉันนั้นสะอาดกว่าการสร้างคำสั่งหลายอย่างสำหรับต้นไม้ที่มีโครงสร้างแตกต่างกัน .... ในสาระสำคัญการเรียกต้นไม้ด้านบนนั้นผิดเล็กน้อยมันเลือกมากขึ้นจาก awnser ของ @ ganaraj ของ "แม่แบบเรียกซ้ำ" แต่ทำให้เราสามารถ กำหนดแม่แบบที่เราต้องการต้นไม้

(คุณสามารถทำได้ด้วยเทมเพลตที่ใช้แท็กสคริปต์ แต่ก็ยังต้องนั่งอยู่นอกโหนดต้นไม้จริงและมันก็ยังรู้สึกยูก ... )

มาถึงที่นี่เพื่อทางเลือกอื่น ...


UPDATE: ขณะที่ 1.5 คำสั่งแบบเรียกซ้ำได้รับการสนับสนุนใน Angular บ้าง สิ่งนี้แคบลงกรณีการใช้งานสำหรับ dotjem / angular-tree อย่างมาก
เจนส์

3

คุณสามารถลองกับตัวอย่างAngular-Tree-DnDกับ Angular-Ui-Tree แต่ฉันแก้ไขเข้ากันได้กับตารางตารางรายการ

  • สามารถลากและวางได้
  • คำสั่งฟังก์ชั่นเพิ่มเติมสำหรับรายการ(ถัดไปก่อนหน้า getChildren, ... )
  • กรองข้อมูล
  • OrderBy (ver)

ขอบคุณ. ฉันต้องการให้ลาก & วางและสิ่งนี้ดูเหมือนจะเป็นทางออกเดียวกับสิ่งนั้น!
Doug

2

ขึ้นอยู่กับ @ganaraj 's คำตอบและ @ dnc253' s คำตอบผมเพิ่งทำง่าย 'สั่ง' สำหรับโครงสร้างมีการเลือกการเพิ่มลบและแก้ไขคุณสมบัติ

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: '=',
        }
    }
});

0

ใช่มันเป็นไปได้อย่างแน่นอน คำถามที่นี่อาจถือว่า 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 1.x เช่นกัน

นี่คือตัวอย่างที่สมบูรณ์: http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0


0

คุณสามารถใช้ angular-recursion-injector สำหรับสิ่งนั้น: https://github.com/knyga/angular-recursion-injector

ช่วยให้คุณสามารถทำรังแบบลึกได้ไม่ จำกัด ด้วยการปรับสภาพ ทำการคอมไพล์ซ้ำหากจำเป็นและคอมไพล์องค์ประกอบที่ถูกต้องเท่านั้น ไม่มีเวทมนตร์ในรหัส

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

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

หนึ่งในสิ่งที่ช่วยให้มันทำงานได้เร็วขึ้นและง่ายขึ้นแล้วโซลูชันอื่น ๆ คือส่วนต่อท้าย "--recursion"


0

เมื่อโครงสร้างต้นไม้มีขนาดใหญ่ 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

-3

ไม่ซับซ้อน

<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"
    }];


}]);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.