javascript - Apply filtering to a hierarchical structure -


i have built following angular app shows hierarchy:

and trying insert text box on top of hierarchy. filter data @ bottom. have tried couple of examples filters have not had luck far.

what want utilize angular binding when user starts typing text box, dynamically expand , collapse hierarchy , highlight matches.

looking advise on best way tackle this. note hierarchy can big , have around 3000 records.

angular.module('helloworldapp', [])     .controller('helloworldcontroller', function($scope) {        $scope.mp6root = [];      $scope.mp6data = [];        var data = [      {          "cls": "l2-013551",          "clsnm": "fashion dolls",          "subct": "l3-001793",          "subctnm": "fashion dolls , accessories",          "ct": "l4-000429",          "ctnm": "dolls games puzzles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006472",          "clsnm": "fashion doll accs",          "subct": "l3-001793",          "subctnm": "fashion dolls , accessories",          "ct": "l4-000429",          "ctnm": "dolls games puzzles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-014668",          "clsnm": "activities",          "subct": "l3-001793",          "subctnm": "fashion dolls , accessories",          "ct": "l4-000429",          "ctnm": "dolls games puzzles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-014667",          "clsnm": "storage",          "subct": "l3-001793",          "subctnm": "fashion dolls , accessories",          "ct": "l4-000429",          "ctnm": "dolls games puzzles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-014675",          "clsnm": "fashion doll playset",          "subct": "l3-001793",          "subctnm": "fashion dolls , accessories",          "ct": "l4-000429",          "ctnm": "dolls games puzzles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006476",          "clsnm": "role play fashion , toy",          "subct": "l3-001793",          "subctnm": "fashion dolls , accessories",          "ct": "l4-000429",          "ctnm": "dolls games puzzles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-014677",          "clsnm": "core ps figure w playset",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006508",          "clsnm": "core ps musical instrument",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-014788",          "clsnm": "wagons toys",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006536",          "clsnm": "riding toys foot floor",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-014678",          "clsnm": "core ps puzzle",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006506",          "clsnm": "core ps figure playset",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006509",          "clsnm": "core ps other toys",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006511",          "clsnm": "core ps talking sound",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006507",          "clsnm": "core ps learning toy",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006510",          "clsnm": "core ps roleplay",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006512",          "clsnm": "core ps vehicles",          "subct": "l3-001798",          "subctnm": "core preschool toys",          "ct": "l4-000428",          "ctnm": "preschool",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006585",          "clsnm": "diecast med lg scale vehicles",          "subct": "l3-001818",          "subctnm": "diecast , playsets",          "ct": "l4-000425",          "ctnm": "act figs construction vehicles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006587",          "clsnm": "diecast playsets",          "subct": "l3-001818",          "subctnm": "diecast , playsets",          "ct": "l4-000425",          "ctnm": "act figs construction vehicles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006586",          "clsnm": "diecast mini vehicles",          "subct": "l3-001818",          "subctnm": "diecast , playsets",          "ct": "l4-000425",          "ctnm": "act figs construction vehicles",          "seg": "l5-000031",          "segnm": "toys",          "area": "l6-000004",          "areanm": "hardlines"      },      {          "cls": "l2-006798",          "clsnm": "vacuums upright bagless",          "subct": "l3-001851",          "subctnm": "floor cleaning",          "ct": "l4-000449",          "ctnm": "home electrics",          "seg": "l5-000054",          "segnm": "hard home",          "area": "l6-000012",          "areanm": "in , outdoor home"      },      {          "cls": "l2-006795",          "clsnm": "vacuums hand",          "subct": "l3-001851",          "subctnm": "floor cleaning",          "ct": "l4-000449",          "ctnm": "home electrics",          "seg": "l5-000054",          "segnm": "hard home",          "area": "l6-000012",          "areanm": "in , outdoor home"      },      {          "cls": "l2-006791",          "clsnm": "floor deep cleaner chemicals",          "subct": "l3-001851",          "subctnm": "floor cleaning",          "ct": "l4-000449",          "ctnm": "home electrics",          "seg": "l5-000054",          "segnm": "hard home",          "area": "l6-000012",          "areanm": "in , outdoor home"      },      {          "cls": "l2-006796",          "clsnm": "vacuums stick",          "subct": "l3-001851",          "subctnm": "floor cleaning",          "ct": "l4-000449",          "ctnm": "home electrics",          "seg": "l5-000054",          "segnm": "hard home",          "area": "l6-000012",          "areanm": "in , outdoor home"      },      {          "cls": "l2-012895",          "clsnm": "floor steam mops",          "subct": "l3-001851",          "subctnm": "floor cleaning",          "ct": "l4-000449",          "ctnm": "home electrics",          "seg": "l5-000054",          "segnm": "hard home",          "area": "l6-000012",          "areanm": "in , outdoor home"      }]      ;                $scope.loadmp6datatomemory = function(data) {            angular.foreach(data, function (value, key) {                if ($.inarray(value.area, $scope.mp6root) === -1) {                  $scope.mp6root.push(value.area);              }                            addtomap(value.cls, value.clsnm, "");              addtomap(value.subct, value.subctnm, value.cls);              addtomap(value.ct, value.ctnm, value.subct);              addtomap(value.seg, value.segnm, value.ct);              addtomap(value.area, value.areanm, value.seg);          });      }        addtomap = function (pkey, pname, pchild) {          if (!$scope.mp6data[pkey]) {              cset = [];              $scope.mp6data[pkey] = { name: pname, children: cset };          } else {              if ($.inarray(pchild, $scope.mp6data[pkey].children) === -1) {                  $scope.mp6data[pkey].children.push(pchild);              }          }      }        $scope.expandmp6 = function (pkey) {          if (pkey) {              mp = $scope.mp6data[pkey];              return {                  name: mp.name,                  children: mp.children,                  visible: false              }          }      }          $scope.loadmp6datatomemory(data);        $scope.l5visible = false;      $scope.l4visible = false;      $scope.l3visible = false;      $scope.l2visible = false;    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>  <div class="container" ng-app="helloworldapp" ng-controller="helloworldcontroller">                <div class="md-grid">                  <ul class="md-list">                      <li class="md-list-item-text" ng:repeat="l6 in mp6root" ng-click="l5visible = !l5visible; $event.stoppropagation();">                          l6 {{expandmp6(l6).name}}                          <ul class="md-list" ng-show="l5visible">                              <li class="md-list-item-text" ng:repeat="l5 in expandmp6(l6).children" ng-click="l4visible = !l4visible; $event.stoppropagation();">                                  l5 {{expandmp6(l5).name}}                                  <ul class="md-list" ng-show="l4visible">                                      <li class="md-list-item-text" ng:repeat="l4 in expandmp6(l5).children" ng-click="l3visible = !l3visible; $event.stoppropagation();">                                          l4 {{expandmp6(l4).name}}                                          <ul class="md-list" ng-show="l3visible">                                              <li class="md-list-item-text" ng:repeat="l3 in expandmp6(l4).children" ng-click="l2visible = !l2visible; $event.stoppropagation();">                                                  l3 {{expandmp6(l3).name}}                                                  <ul class="md-list" ng-show="l2visible">                                                      <li class="md-list-item-text" ng:repeat="l2 in expandmp6(l3).children">                                                          l2 {{expandmp6(l2).name}}                                                      </li>                                                  </ul>                                              </li>                                          </ul>                                      </li>                                  </ul>                              </li>                          </ul>                      </li>                  </ul>              </div>              </div>

edit: filter thinking of not seem adapt on way ive structured html: how filter data text box in angularjs

if html structure needs changed i'm open suggestions.

first, should create nested directive display tree. if there 7 levels display ? first write recursive directive, reduce code size.

for data filtering part, can use input ng-model-options="{debounce: 300}" combined ng-change="filterfunction()" filtering applies 300ms after user has finished writing search. filterfunction() pretty easy write once data structured in hierarchical form, , can change object state indicate directive wether should displayed, , wether children should displayed.

the result looks this:

maincontroller.js

var app = angular.module('app', []); app.controller('maincontroller', [function () {   var ctrl = this;   ctrl.search = '';   inithierarchies(); // function transforms data in hierarchical form   // filterhierarchies called everytime user changed search input   ctrl.filterhierarchies = function () {     ctrl.filteredhierarchies = hierarchiesfilter(ctrl.hierarchies, ctrl.search).hierarchies;   }   ctrl.filterhierarchies(); // init filteredhierarchies data.    // function filters hierarchy. recursive function   function hierarchiesfilter(hierarchies, search) {     if (!hierarchies || !hierarchies.length) {       return { hierarchies: [], hasexpandedchildren: false};     }     console.log(hierarchies, search);     var oneisexpanded = false;     (var = 0; < hierarchies.length; i++) {       hierarchies[i].showchildren = false;       if (search.length) {         var rx = new regexp(search, 'i');         if (hierarchies[i].name.match(rx)) {           oneisexpanded = true;         }       }       // if node has children expanded, need display children       // should highlighted visible       var hasexpandedchildren = hierarchiesfilter(hierarchies[i].children, search).hasexpandedchildren;       if (hasexpandedchildren) {         hierarchies[i].showchildren = true;         oneisexpanded = true;       }     }     return { hierarchies: hierarchies, hasexpandedchildren: oneisexpanded };   };    // function transform array data hierarchical structure   function inithierarchies() {     var data = getdata();     var mp6data = {};     var mp6root = [];      angular.foreach(data, function (value, key) {         if (mp6root.indexof(value.area) === -1) {             mp6root.push(value.area);         }          addtomap(value.cls, value.clsnm, "");         addtomap(value.subct, value.subctnm, value.cls);         addtomap(value.ct, value.ctnm, value.subct);         addtomap(value.seg, value.segnm, value.ct);         addtomap(value.area, value.areanm, value.seg);     });      function addtomap(pkey, pname, pchild) {         if (!mp6data[pkey]) {             mp6data[pkey] = { name: pname, childrenkeys: [] };         } else {             if (mp6data[pkey].childrenkeys.indexof(pchild) === -1) {                 mp6data[pkey].childrenkeys.push(pchild);             }         }     }      function buildhierarchicalstructure(childrenkeys) {       var builtchildren = [];       (var = 0; < childrenkeys.length; i++) {         builtchildren.push({           name: mp6data[childrenkeys[i]].name,           children: buildhierarchicalstructure(mp6data[childrenkeys[i]].childrenkeys)         });       }       return builtchildren;     }      (var = 0; < mp6data.length; i++) {       mp6data[i].showchildren = true;     }     ctrl.hierarchies = buildhierarchicalstructure(mp6root);   } }]); 

hierarchy.directive.js

app.directive('hierarchy', ['recursionhelper', function (recursionhelper) {   return {     template: '<div><div ng-click="hierarchyctrl.ngmodel.showchildren = !hierarchyctrl.ngmodel.showchildren">{{ hierarchyctrl.ngmodel.name }}</div><ul ng-if="hierarchyctrl.ngmodel.children && (hierarchyctrl.ngmodel.showchildren)"><li ng-repeat="element in hierarchyctrl.ngmodel.children"><hierarchy ng-model="element"></hierarchy></li></ul></div>',     restrict: 'e',     scope: { ngmodel: '=' },     controller: ['$scope', function($scope) { this.ngmodel = $scope.ngmodel; }],     controlleras: 'hierarchyctrl',     compile: function (element) {        return recursionhelper.compile(element);     },   }; }]); 

index.html

<body> <h1>hello plunker!</h1> <div ng-controller="maincontroller mainctrl">   <input type="text" ng-model="mainctrl.search" ng-model-options="{debounce: 300}" ng-change="mainctrl.filterhierarchies()" />   <ul>     <li ng-repeat="hierarchy in mainctrl.filteredhierarchies"><hierarchy ng-model="hierarchy"></hierarchy></li>   </ul> </div> </body> 

plunker example: https://plnkr.co/edit/1jiiiwkduzy4tm7sm79f?p=preview

i'll let write code part highlight text matches search, it's pretty easy transform function filtering. hint: in hierarchiesfilter() function, can add htmlhighlighted property each node wrap matching text between <strong> , </strong> tags.

as not exact behavior looking for, can tweak filter function display want when user changes search.

some code (the recursion helper) comes post: recursion in angular directives


Comments

Popular posts from this blog

javascript - Create a stacked percentage column -

Optimising Firebase database by automatically overwriting data -

javascript - Angular UI-Grid customTemplate directive causing rows to load slowly/? -