क्या एंगुलर के साथ ट्री व्यू बनाना संभव है?


177

मैं एक वेब ऐप में ट्री स्ट्रक्चर में डेटा प्रदर्शित करना चाहता हूं। मैं इस कार्य के लिए कोणीय का उपयोग करने की उम्मीद कर रहा था।

एनजी-रिपीट की तरह लगता है कि मुझे नोड्स की सूची के माध्यम से पुनरावृति करने की अनुमति होगी, लेकिन जब किसी दिए गए नोड की गहराई बढ़ जाती है तो मैं कैसे नेस्टिंग कर सकता हूं?

मैंने निम्न कोड की कोशिश की , लेकिन HTML का ऑटो-एस्कॉर्ट इसे काम करने से रोक रहा है। साथ ही, अंतिम उल टैग गलत जगह पर है।

मुझे पूरा यकीन है कि मैं पूरी तरह से गलत तरीके से इस समस्या के बारे में जा रहा हूँ।

कोई विचार?


मैंने अभी एक बहुत ही सामान्य तरीके से एक अलग सवाल पर इसका जवाब दिया: stackoverflow.com/questions/14430655/…
tilgovi

जवाबों:


231

इस बेला पर एक नजर

मूल: http://jsfiddle.net/brendanowen/uXbn6/8/

अपडेट किया गया: http://jsfiddle.net/animaxf/uXbn6/4779/

यह आपको एक tree like structureकोणीय का उपयोग करने के तरीके का एक अच्छा विचार देना चाहिए । यह html में पुनरावृत्ति का उपयोग करने का प्रकार है!


94
अपने स्रोत का वर्णन क्यों नहीं करते ? आपने उस थ्रेड में एक पोस्ट लिखी है, और अब आप उसमें अपने नाम के साथ एक यूआरएल पोस्ट कर रहे हैं?
जानूस ट्रॉल्सन

5
यहां एक समान संस्करण है (मुझे लगता है), सिवाय इसके कि यह बहुत तेज लोड करता है (मेरे लिए कम से कम), क्योंकि इसमें सीएसएस अनुभाग में ट्विटर बूटस्ट्रैप इनबिल्ड नहीं है। jsfiddle.net/brendanowen/uXbn6/8
KajMagnus

10
यार आपको अपना स्रोत बताना चाहिए।
अजाक्स 3.14 15

46
मैं वास्तव में लगातार लोगों को इस पर टिप्पणी करते हुए थक गया था कि URL में मेरा नाम है (और इसलिए यह साहित्यिक चोरी है!)। दुर्भाग्य से jsfiddle कैसे काम करता है। यदि आप लॉग इन करते समय कुछ कांटा करते हैं, तो अपना उपयोगकर्ता नाम बरकरार रखता है। यह कहते हुए कि मैं अब मूल URL से जुड़ गया हूं। किसी उत्तर को गलत होने पर डाउनवोट करें - उत्तर इस परिदृश्य में एक चीज़ के साथ सही होता है, जो कि बैकअप URL मैं लगता है कि उसमें मेरा नाम शामिल है।
गणराज

5
मैंने अभी-अभी आपके संस्करण में पतन और विस्तार बटन जोड़ा: jsfiddle.net/uXbn6/639
jbaylina

77

यदि आप बूटस्ट्रैप CSS का उपयोग कर रहे हैं ...

मैंने एक बूटस्ट्रैप "नेवी" सूची के आधार पर AngularJS के लिए एक सरल पुन: प्रयोग करने योग्य वृक्ष नियंत्रण (निर्देश) बनाया है। मैंने अतिरिक्त इंडेंटेशन, आइकन और एनीमेशन जोड़े। HTML विशेषताएँ विन्यास के लिए उपयोग की जाती हैं।

यह पुनरावृत्ति का उपयोग नहीं करता है।

मैंने इसे कोणीय-बूटस्ट्रैप-नेव-ट्री (आकर्षक नाम, क्या आपको नहीं लगता?) कहा जाता है।

यहाँ एक उदाहरण है , और स्रोत यहाँ है


1
यह सुंदर है, लेकिन चेतावनी दी जाए कि यह कोणीय 1.0.x शाखा पर काम नहीं करता है।
दानिता

3
हां, यह नए एनीमेशन सामान का उपयोग करता है ... को कोणीय 1.1.5 की आवश्यकता होती है (मुझे लगता है?)
निक पर्किन्स

3
अद्यतन: यह अब या तो कोणीय 1.1.5 या कोणीय 1.2.0 के साथ काम करता है, और बूटस्ट्रैप 2 या बूटस्ट्रैप 3 के साथ भी काम करता है
निक पर्किंस

1
FYI करें, यदि Bower का उपयोग करते हैं, तो निक ने अब इसे आसान इंस्टॉल के लिए उपलब्ध कराया है - "bower search angular-bootstrap-nav-tree", और "bower install angular-bootstrap-nav-tree -save" और आप कर चुके हैं।
आर्केल्डन

2
@ निक पर्किन्स - कृपया आप यह बता सकते हैं कि आपके कोणीय-बूटस्ट्रैप-नेव-ट्री में ब्रांच / नोड हटाने के लिए कोई एपीआई क्यों नहीं है। कम से कम, स्रोत के त्वरित निरीक्षण से और अपने परीक्षण / उदाहरणों की जांच करने पर वह विकल्प नहीं दिखता है। यह एक महत्वपूर्ण चूक है, निश्चित रूप से?
आर्केल्डन

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);
        }
    };
});

डेमो के लिए इस प्लंकर को देखें । मुझे यह घोल सबसे अच्छा लगता है क्योंकि:

  1. आपको एक विशेष निर्देश की आवश्यकता नहीं है जो आपके html को कम स्वच्छ बनाता है।
  2. रिकर्सन लॉजिक को रिकर्सियन हेल्पर सेवा में दूर किया जाता है, इसलिए आप अपने निर्देशों को साफ रखें।

अद्यतन: एक कस्टम लिंकिंग फ़ंक्शन के लिए जोड़ा गया समर्थन।


1
यह इतना साफ और शक्तिशाली लगता है, किसी भी विचार क्यों यह angularjs में एक डिफ़ॉल्ट व्यवहार नहीं है?
पॉल

इस तरह "संकलन" का उपयोग करते समय, कोई अतिरिक्त विशेषताओं को दायरे में कैसे जोड़ता है? "लिंक" फ़ंक्शन अब लगता है कि एक बार "संकलन" उपलब्ध नहीं है ...
ब्रायन केंट

1
@ bkent314 मैंने इसके लिए समर्थन जोड़ा। यह अब उसी तरह से लिंकिंग फंक्शंस को स्वीकार करता है जैसे कंपाइल उन्हें वापस कर सकता है। मैंने सेवा के लिए जीथब प्रोजेक्ट भी बनाया।
मार्क लैजेंडिजक

@MarkLagendijk बहुत, बहुत चालाक! आप निर्देश के बाहर की पुनरावृत्ति को अमूर्त करने के लिए कई upvotes के लायक हैं। मैंने जो निर्देश देखे हैं, उनमें से सभी उस तर्क के साथ निराशाजनक रूप से जटिल दिखते हैं। क्या आपके पुनरावर्तन के साथ अपने रिकर्सियन हेल्पर के काम करने का कोई तरीका है?
अजय

मैं वास्तव में सुझाव देता हूं कि आप इस तरह के समाधान पर कुछ डेटा फेंकते हैं - हां, लगभग हर व्यक्ति पुनरावर्ती निर्देशों के साथ पेड़ को लागू करता है, यह आसान है। लेकिन यह एनजी-रिपीट $ डाइजेस्ट के रूप में बेहद धीमा है - एक बार जब आप सैकड़ों नोड्स पर पहुंच जाते हैं, तो यह प्रदर्शन नहीं करता है।
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); 
            });
        };
    }
};
});

मैं इस के साथ प्रयोग कर रहा था, और मैं भी ट्रांसकॉक्शन का उपयोग करना चाहूंगा, क्या आपको लगता है कि यह संभव है?
एल। क्रैबचिन


5

मूल स्रोत के आधार पर एक और उदाहरण , नमूना पेड़ संरचना के साथ पहले से ही (यह देखने के लिए आसान है कि यह आईएमओ कैसे काम करता है) और पेड़ की खोज करने के लिए एक फिल्टर:

JSFiddle


4

इतने सारे महान समाधान, लेकिन मुझे लगता है कि वे एक तरह से या किसी अन्य चीज़ को थोड़ा जटिल करते हैं।

मैं कुछ ऐसा बनाना चाहता था जो @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>

जो मुझे अलग-अलग संरचित पेड़ों के लिए कई निर्देश बनाने की तुलना में क्लीनर है .... सार में ऊपर एक पेड़ को कॉल करना थोड़ा गलत है, यह "पुनरावर्ती टेम्पलेट्स" के @ ग्नाराज के awnser से बहुत अधिक निकालता है, लेकिन हमारे साथ टेम्पलेट को परिभाषित करें जहां हमें पेड़ की आवश्यकता है।

(आप स्क्रिप्ट टैग आधारित टेम्प्लेट के साथ ऐसा कर सकते हैं, लेकिन इसे अभी भी वास्तविक ट्री नोड के ठीक बाहर बैठना है, और यह अभी भी थोड़ा सा महसूस करता है ...)

बस एक और विकल्प के लिए यहां छोड़ दिया ...


अद्यतन: 1.5 पुनरावर्ती निर्देशों के रूप में अब कुछ हद तक मूल रूप से कोणीय में समर्थित हैं। यह डॉटजेम / कोणीय-ट्री के लिए उपयोग के मामलों को काफी हद तक कम कर देता है।
जेन्स

3

आप कोणीय-यूआई-ट्री के साथ कोणीय-ट्री-डीएनडी नमूने के साथ कोशिश कर सकते हैं , लेकिन मैंने संपादित किया, तालिका, ग्रिड, सूची के साथ संगतता।

  • सक्षम ड्रैग एंड ड्रॉप
  • सूची के लिए विस्तारित कार्य निर्देश (अगला, प्रचलित, getChildren, ...)
  • डेटा फ़िल्टर करें।
  • ऑर्डरबाय (क्रिया)

धन्यवाद। मुझे ड्रैग एंड ड्रॉप की आवश्यकता थी, और यह उसी के साथ एकमात्र समाधान प्रतीत होता है!
डग

2

@ त्यागराज के उत्तर के आधार पर , और @ 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>

जावास्क्रिप्ट:

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

हाँ यह संभव है। यहाँ सवाल शायद कोणीय 1.x मानता है, लेकिन भविष्य के संदर्भ के लिए मैं एक कोणीय 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-in-ular


0

आप इसके लिए कोणीय-रिकर्सन-इंजेक्टर का उपयोग कर सकते हैं: 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

जब पेड़ की संरचना बड़ी होती है, तो कोणीय (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>

यह सभी कोणीय डेटा बाइंडिंग को बाईपास करता है और बस पुनरावर्ती टेम्पलेट विधियों के समय के एक अंश में HTML प्रदर्शित करता है।

इस तरह एक मेनू संरचना के साथ (एक लिनक्स फ़ाइल सिस्टम का एक आंशिक फ़ाइल ट्री):

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


}]);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.