2016-01-04 17 views
15

Używam https://github.com/tchatel/angular-treeRepeat i próbuję filtrować węzły, które nie zostały rozwinięte. Więc ja zmodyfikowany kod zawierać filtr angularjs:Filtr AngularJS nie rozwija filtrowanych węzłów

treeRepeat.html:

<p id="expand-collapse-all"> 
    <a href="" ng-click="expandAll()">Expand all</a> 
    <a href="" ng-click="collapseAll()">Collapse all</a> 
</p> 


Filter : <input ng-model="myFilter" type="text"> 

<ul frang-tree> 
    <li frang-tree-repeat="node in treeData | filter:myFilter"> 
     <div><span class="icon" 
       ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}" 
       ng-show="node.children && node.children.length > 0" 
       ng-click="node.collapsed = !node.collapsed"></span> 
      <span class="label" 
       ng-class="{folder: node.children && node.children.length > 0}" 
       ng-bind="node.label" 
       ng-click="action(node)"></span> 
     </div> 
     <ul ng-if="!node.collapsed && node.children && node.children.length > 0" 
      frang-tree-insert-children="node.children | filter:myFilter"></ul> 
    </li> 
</ul> 

To działa zgodnie z oczekiwaniami, jeśli są rozszerzane wszystkich węzłów drzewa: linii 20 na controllers.js:

$scope.treeData = JSON.parse("[ { \"label\": \"root\", \"children\": [ { \"label\": \"folder A\", \"collapsed\": true, \"children\": [ { \"label\": \"folder B\", \"collapsed\": true, \"children\": [ { \"label\": \"file B1\", \"collapsed\": true }, { \"label\": \"file B2\", \"collapsed\": true } ] }, { \"label\": \"file A1\", \"collapsed\": true }, { \"label\": \"file A2\", \"collapsed\": true }, { \"label\": \"file A3\", \"collapsed\": true }, { \"label\": \"file A4\", \"collapsed\": true } ] }, { \"label\": \"folder C\", \"collapsed\": true, \"children\": [ { \"label\": \"folder D\", \"collapsed\": true, \"children\": [ { \"label\": \"folder E\", \"collapsed\": true, \"children\": [ { \"label\": \"file E1\", \"collapsed\": true }, { \"label\": \"file E2\", \"collapsed\": true }, { \"label\": \"file E3\", \"collapsed\": true } ] } ] }, { \"label\": \"folder F\", \"collapsed\": true, \"children\": [ { \"label\": \"file F1\", \"collapsed\": true }, { \"label\": \"file F2\", \"collapsed\": true } ] }, { \"label\": \"file C1\", \"collapsed\": true } ] }, { \"label\": \"folder G\", \"collapsed\": true, \"children\": [ { \"label\": \"file G1\", \"collapsed\": true }, { \"label\": \"file G2\", \"collapsed\": true }, { \"label\": \"file G3\", \"collapsed\": true }, { \"label\": \"file G4\", \"collapsed\": true } ] }, { \"label\": \"folder H\", \"collapsed\": true, \"children\": [ { \"label\": \"file H1\", \"collapsed\": true }, { \"label\": \"file H2\", \"collapsed\": true }, { \"label\": \"file H3\", \"collapsed\": true } ] } ] } ]"); 

Ale jeśli węzły są zwinięte, dopasowane węzły rozszerzone/widoczne. Drzewo pozostaje zwinięte. Config dla zwiniętych węzłów: Linia 21 na controllers.js:

$scope.treeData = JSON.parse("[ { \"label\": \"root\", \"children\": [ { \"label\": \"folder A\", \"collapsed\": true, \"children\": [ { \"label\": \"folder B\", \"collapsed\": true, \"children\": [ { \"label\": \"file B1\", \"collapsed\": true }, { \"label\": \"file B2\", \"collapsed\": true } ] }, { \"label\": \"file A1\", \"collapsed\": true }, { \"label\": \"file A2\", \"collapsed\": true }, { \"label\": \"file A3\", \"collapsed\": true }, { \"label\": \"file A4\", \"collapsed\": true } ] }, { \"label\": \"folder C\", \"collapsed\": true, \"children\": [ { \"label\": \"folder D\", \"collapsed\": true, \"children\": [ { \"label\": \"folder E\", \"collapsed\": true, \"children\": [ { \"label\": \"file E1\", \"collapsed\": true }, { \"label\": \"file E2\", \"collapsed\": true }, { \"label\": \"file E3\", \"collapsed\": true } ] } ] }, { \"label\": \"folder F\", \"collapsed\": true, \"children\": [ { \"label\": \"file F1\", \"collapsed\": true }, { \"label\": \"file F2\", \"collapsed\": true } ] }, { \"label\": \"file C1\", \"collapsed\": true } ] }, { \"label\": \"folder G\", \"collapsed\": true, \"children\": [ { \"label\": \"file G1\", \"collapsed\": true }, { \"label\": \"file G2\", \"collapsed\": true }, { \"label\": \"file G3\", \"collapsed\": true }, { \"label\": \"file G4\", \"collapsed\": true } ] }, { \"label\": \"folder H\", \"collapsed\": true, \"children\": [ { \"label\": \"file H1\", \"collapsed\": true }, { \"label\": \"file H2\", \"collapsed\": true }, { \"label\": \"file H3\", \"collapsed\": true } ] } ] } ]"); 

Plunkr: https://plnkr.co/edit/CtXlRfdreolTTc018c0A?p=preview

Czy muszę ręcznie rozszerzać węzły jako typów użytkowników lub ma kanciasty config mogę używać, aby rozwinąć te węzły?

Próbowałem dodanie funkcji niestandardowej, że pożary typy użytkowników Everytime:

function matchChildNode(objData , parentNode) { 

     angular.forEach(objData, function(childNode, key) { 

       var searchText = ""; 
       //AngularJS does not initialise the searchText var until used. As the function is re-initialised for every node 
       //need to check if is undefined 
       if ($scope.searchText == undefined) { 
        searchText = "" 
       } else { 
        searchText = $scope.searchText 
       } 

       if (searchText.toLowerCase() === childNode.label.toLowerCase()) { 
        parentNode.collapsed = false 
       }  

       matchChildNode(childNode.children , childNode); 

    }); 
    } 
} 

Ale jest to bardzo nieefektywne, ponieważ przemierza całą strukturę drzewa dla każdego słowa kluczowego typów użytkowników. Działa to również dla dokładnie dopasowanego tekstu: searchText.toLowerCase() === childNode.label.toLowerCase(). Próbowałem użyć contains zamiast === bez powodzenia.

plnkr src:

app.css (removed due to stackoverflow 30000 character limitation when asking questions) 

directives.js (removed due to stackoverflow 30000 character limitation when asking questions) 

filter.js : 

'use strict'; 

angular.module('app.filters', []); 

index.html : 


<!doctype html> 
<html lang="en" ng-app="app"> 
<head> 
    <meta charset="utf-8"> 
    <title>treeRepeat demo</title> 
    <link rel="stylesheet" href="app.css"/> 
</head> 

<body> 

    <h1>treeRepeat</h1> 
    <div id="menu" ng-controller="MenuCtrl"> 
     <ul> 
      <li ng-repeat="item in menu" ng-class="{selected: item == getCurrentMenuItem()}"><a href="#/{{item.index}}">{{item.shortLabel}}</a></li> 
     </ul> 
     <h2>{{getCurrentMenuItem().fullLabel}}</h2> 
    </div> 

    <ng-view></ng-view> 

    <script src="angular.js"></script> 
    <script src="angular-route.js"></script> 
    <script src="app.js"></script> 
    <script src="services.js"></script> 
    <script src="controllers.js"></script> 
    <script src="filters.js"></script> 
    <script src="directives.js"></script> 
</body> 
</html> 

services.js : 

'use strict'; 

angular.module('app.services', []) 
    .constant('menu', []); 

treerepeat.html : 

<p id="expand-collapse-all"> 
    <a href="" ng-click="expandAll()">Expand all</a> 
    <a href="" ng-click="collapseAll()">Collapse all</a> 
</p> 


Filter : <input ng-model="myFilter" type="text"> 

<ul frang-tree> 
    <li frang-tree-repeat="node in treeData | filter:myFilter"> 
     <div><span class="icon" 
       ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}" 
       ng-show="node.children && node.children.length > 0" 
       ng-click="node.collapsed = !node.collapsed"></span> 
      <span class="label" 
       ng-class="{folder: node.children && node.children.length > 0}" 
       ng-bind="node.label" 
       ng-click="action(node)"></span> 
     </div> 
     <ul ng-if="!node.collapsed && node.children && node.children.length > 0" 
      frang-tree-insert-children="node.children | filter:myFilter"></ul> 
    </li> 
</ul> 
+0

Nie każdy scenariusz działa w rozbudowanych węzłów. Rozwiń wszystkie i wyszukaj "A". Powinien działać tak, jakbyś szukał "pliku A". Dobrze? – Valijon

+0

@Valijon tak, wydaje się działać, tylko problem polega na tym, że gdy użytkownik usuwa cały tekst z pola tekstowego drzewo pozostaje rozwinięte, powinienem móc sprawdzić ten scenariusz i po prostu ponownie załadować drzewo: https://plnkr.co/edit/CtXlRfdreolTTc018c0A ? p = preview –

+0

@ blue-sky Czy chcesz filtrować tylko te węzły, które nie zostały rozwinięte? –

Odpowiedz

4

Można to osiągnąć poprzez zastosowanie filtra z predicate funkcji.

funkcja (wartość, indeks): Funkcja predykatu może być używana do zapisu dowolnych filtrów. Funkcja jest wywoływana dla każdego elementu tablicy. Końcowym wynikiem jest tablica elementów, do których predykat powrócił.

  1. Tworzenie funkcji predykatu

$scope.getFilter = function(value, index){ 
    if(!$scope.myFilter || $scope.myFilter === '' && (value.label === 'root')){ 
     $scope.collapseAll(); 
     return true; 
    } 
    if (findMatch(value, $scope.myFilter)){ 
     value.collapsed = false; 
     return true; 
    }else{ 
     value.collapsed = true; 
     return false; 
    } 
} 
  1. utworzyć funkcję, która sprawdza, czy aktualna pozycja jest mecz (ta funkcja jest wywoływana z funkcji predykatu)

function findMatch(value, filter) { 
    var found = false; 
    if(value.label.toLowerCase().indexOf(filter) > -1){ 
     value.collapsed = false; 
     found = true; 
    } 
    if(!value.children || value.children.length===0){ 
     if(value.collapsed!== undefined){ 
      value.collapsed = true; 
      return found; 
      } 
     } 
     for (var i = 0; i < value.children.length; i++) { 
      if (value.children[i].label.toLowerCase().indexOf(filter) > -1) { 
       //match found 
       value.collapsed = false; 
       value.children[i].collapsed = false; 
       found = true; 
      } else { 
       //check child items 
       if(value.children[i].children && value.children[i].children.length>0) 
       { 
        if (findMatchingChildren(value.children[i].children, filter)) { 
        value.children[i].collapsed = false; 
        found = true; 
        }else{ 
        value.children[i].collapsed = true; 
        } 
       } 
      } 
     } 

     return found; 
} 

3. Utwórz funkcję, która wykonuje pętle przez elementy podrzędne bieżącego elementu, ta funkcja wywołuje funkcję z kroku 2. Kończy się funkcją rekursywną, która sprawdza wszystkie poziomy.

function findMatchingChildren(children, filter) { 
    var found = false; 
    for (var i = 0; i < children.length; i++) { 
     if (findMatch(children[i], filter)) { 
      children[i].collapsed = false; 
      found = true; 
     }else{ 
      children[i].collapsed = true; 
     } 
    } 
    return found; 
} 

Jak znaleźć pasujące elementy, które należy ustawić wartość false

collapsed proszę zobaczyć przykład roboczych here

HTML

<p id="expand-collapse-all"> 
    <a href="" ng-click="expandAll()">Expand all</a> 
    <a href="" ng-click="collapseAll()">Collapse all</a> 
</p> 
Filter : <input ng-model="myFilter" type="text"> 
<ul frang-tree> 
    <li frang-tree-repeat="node in treeData | filter:getFilter"> 
     <div> 
      <span class="icon" 
       ng-class="{collapsed: node.collapsed, expanded: !node.collapsed}" 
       ng-show="node.children && node.children.length > 0" 
       ng-click="node.collapsed = !node.collapsed"></span> 
      <span class="label" 
       ng-class="{folder: node.children && node.children.length > 0}" 
       ng-bind="node.label" 
       ng-click="action(node)"></span> 
     </div> 
     <ul ng-if="!node.collapsed && node.children && node.children.length>0" frang-tree-insert-children="node.children | filter:myFilter"></ul> 
</li> 
</ul> 
+0

dzięki, ale jest "plnkr.co/edit/CtXlRfdreolTTc018c0A?p=preview - blue-sky 7 stycznia o 13:22" nie jest prostsze rozwiązanie? –

0

Rozważmy wodowania moduł angular-treeRepeat całkowicie i przy użyciu szablonu rekurencyjnej. Ben Foster wyjaśnia, jak to działa w tym blog post i jest to rozwiązanie o wiele bardziej eleganckie (i mniej szalenie złożone), jak sądzę.

przykład Praca z filtrem:JSFiddle

HTML:

<div ng-app="app" ng-controller='AppCtrl'> 
    Filter: <input ng-model="myFilter" type="text"> 
    <script type="text/ng-template" id="categoryTree"> 
    {{ category.title }} 
    <ul ng-show="category.categories"> 
     <li 
     ng-repeat="category in category.categories | filter:myFilter" 
     ng-include="'categoryTree'"> 
     </li> 
    </ul> 
    </script> 
    <ul> 
    <li 
     ng-repeat="category in categories | filter:myFilter" 
     ng-include="'categoryTree'"> 
    </li> 
    </ul> 
</div> 

JavaScript:

var app = angular.module('app', []) 
    .controller('AppCtrl', function($scope) { 
    $scope.categories = [{ 
     title: 'Computers', 
     categories: [{ 
      title: 'Laptops', 
      categories: [{ 
      title: 'Ultrabooks' 
      },{ 
      title: 'Macbooks' 
      }] 
     },{ 
      title: 'Desktops' 
     },{ 
      title: 'Tablets', 
      categories: [{ 
      title: 'Apple' 
      },{ 
      title: 'Android' 
      }] 
     } 
     ] 
    },{ 
     title: 'Printers' 
    }]; 
    }); 
Powiązane problemy