frontend CHANGE refactor configuration view

provide new service to handle tree from several components
diff --git a/frontend/config/config.component.html b/frontend/config/config.component.html
index 003bf94..de5a21b 100644
--- a/frontend/config/config.component.html
+++ b/frontend/config/config.component.html
@@ -48,29 +48,16 @@
             <th class="item_right" >Data</th>
         </tr>
     </table>
-    <div>
-        <img *ngIf="activeSession.dataVisibility!='root'" class="icon_action" (click)="rpcGet(false)"
-            src="assets/netopeer/icons/show_children.svg" alt="v" title="expand roots"
-            onmouseover="this.src='assets/netopeer/icons/show_children_active.svg'"
-            onmouseout="this.src='assets/netopeer/icons/show_children.svg'"/>
-        <img *ngIf="activeSession.dataVisibility!='all'" class="icon_action" (click)="rpcGet(true)"
-            src="assets/netopeer/icons/show_all.svg" alt="w" title="expand all"
-            onmouseover="this.src='assets/netopeer/icons/show_all_active.svg'"
-            onmouseout="this.src='assets/netopeer/icons/show_all.svg'"/>
-        <img *ngIf="activeSession.dataVisibility!='none'" class="icon_action" (click)="setDataVisibility('none')"
-            src="assets/netopeer/icons/collapse.svg" alt="x" title="collapse"
-            onmouseover="this.src='assets/netopeer/icons/collapse_active.svg'"
-            onmouseout="this.src='assets/netopeer/icons/collapse.svg'"/>
-        <span *ngIf="activeSession.statusVisibility"><a (click)="invertStatus()">hide</a> status data</span>
-        <span *ngIf="!activeSession.statusVisibility"><a (click)="invertStatus()">show</a> status data</span>
-        <!--<span><input type="checkbox" name="statusVisibility" [checked]="activeSession.statusVisibility" (change)="invertStatus()"/> include status data</span>-->
-    </div><br/>
-    <div class="loading" *ngIf="loading">
+    <div class="loading" *ngIf="treeService.loading">
         <span>Retrieving data ...</span>
         <div><netopeer-loading spinner="true"></netopeer-loading></div>
     </div>
     <ng-container *ngIf="activeSession.dataVisibility!='none' && activeSession.data">
     <div id="config-data">
+        <div>
+            <span *ngIf="activeSession.statusVisibility"><a (click)="invertStatus()">hide</a> status data</span>
+            <span *ngIf="!activeSession.statusVisibility"><a (click)="invertStatus()">show</a> status data</span>
+        </div>
         <tree-view [node]="activeSession.data"></tree-view>
         <!--  <pre *ngIf="activeSession.data['schemaChildren']">{{activeSession.data['schemaChildren'] | json}}</pre> -->
     </div>
diff --git a/frontend/config/config.component.ts b/frontend/config/config.component.ts
index af12bff..89205b5 100644
--- a/frontend/config/config.component.ts
+++ b/frontend/config/config.component.ts
@@ -1,24 +1,142 @@
-import {Component, OnInit} from '@angular/core';
+import {Component, Injectable, OnInit} from '@angular/core';
 import {Router} from '@angular/router';
 
 import {ModificationsService} from './modifications.service';
 import {SessionsService} from './sessions.service';
 import {Session} from './session';
 
+@Injectable()
+export class TreeService {
+    loading = false;
+
+    constructor(private sessionsService: SessionsService, private modsService: ModificationsService) {}
+
+    rpcGet(activeSession, all: boolean) {
+        if (activeSession.data) {
+            if ((all && activeSession.dataVisibility == 'all') ||
+                (!all && activeSession.dataVisibility == 'root')) {
+                return;
+            }
+        }
+        this.loading = true;
+        delete activeSession.data;
+        this.sessionsService.rpcGetSubtree(activeSession.key, all).subscribe(result => {
+            if (result['success']) {
+                for (let iter of result['data']) {
+                    this.modsService.setDirty(activeSession, iter);
+                }
+                activeSession.data = {};
+                activeSession.data['path'] = '/';
+                activeSession.data['info'] = {};
+                activeSession.data['info']['path'] = '/';
+                activeSession.data['children'] = result['data'];
+                if (all) {
+                    activeSession.dataVisibility = 'all';
+                } else {
+                    activeSession.dataVisibility = 'root';
+                }
+                console.log(activeSession.data);
+            }
+            this.sessionsService.storeData();
+            this.loading = false;
+        });
+    }
+
+    expandable(node): boolean {
+        if (node['info']['type'] == 1 || /* container */
+            node['info']['type'] == 16) { /* list */
+                return true;
+        }
+        return false;
+    }
+
+    hasHiddenChild(node, clean=false): boolean {
+        if (!clean && 'hasHiddenChild' in node) {
+            return node['hasHiddenChild'];
+        }
+        node['hasHiddenChild'] = false;
+        if (!this.expandable(node)) {
+            /* terminal node (leaf or leaf-list) */
+            return node['hasHiddenChild'];
+        } else if (!('children' in node)) {
+            /* internal node without children */
+            node['hasHiddenChild'] = true;
+        } else {
+            /* go recursively */
+            for (let child of node['children']) {
+                if (this.hasHiddenChild(child, clean)) {
+                    node['hasHiddenChild'] = true;
+                    break;
+                }
+            }
+        }
+        return node['hasHiddenChild'];
+    }
+
+    updateHiddenFlags(activeSession) {
+        let mixed = false;
+        let rootsonly = true;
+        for (let root of activeSession.data['children']) {
+            if (this.hasHiddenChild(root, true)) {
+                mixed = true;
+            } else {
+                rootsonly = false;
+            }
+        }
+        if (mixed) {
+            if (rootsonly) {
+                activeSession.dataVisibility = 'root';
+            } else {
+                activeSession.dataVisibility = 'mixed';
+            }
+        }
+    }
+
+    collapse(activeSession, node = null) {
+        if (node) {
+            delete node['children'];
+            activeSession.dataVisibility = 'mixed';
+        } else {
+            for (let root of activeSession.data['children']) {
+                delete root['children'];
+            }
+            activeSession.dataVisibility = 'root';
+        }
+        this.updateHiddenFlags(activeSession);
+        this.sessionsService.storeData();
+    }
+
+    expand(activeSession, node, all: boolean) {
+        node['loading'] = true;
+        this.sessionsService.rpcGetSubtree(activeSession.key, all, node['path']).subscribe(result => {
+            if (result['success']) {
+                for (let iter of result['data']['children']) {
+                    this.modsService.setDirty(activeSession, iter);
+                }
+                node['children'] = result['data']['children'];
+                this.updateHiddenFlags(activeSession);
+                delete node['loading'];
+                this.sessionsService.storeData();
+            }
+        });
+    }
+}
+
 @Component({
     selector: 'netopeer-config',
     templateUrl: './config.component.html',
-    styleUrls: ['./config.component.scss']
+    styleUrls: ['./config.component.scss'],
+    providers: [ModificationsService, TreeService]
 })
 
 export class ConfigComponent implements OnInit {
     title = 'Configuration';
     activeSession: Session;
     err_msg = "";
-    loading = false;
 
     constructor(private sessionsService: SessionsService,
                 private modsService: ModificationsService,
+                private treeService: TreeService,
                 private router: Router) {}
 
     addSession() {
@@ -27,10 +145,10 @@
 
     reloadData() {
         this.activeSession.data = null;
-        if (this.activeSession.dataVisibility == 'all') {
-            this.rpcGet(true);
-        } else if(this.activeSession.dataVisibility == 'root') {
-            this.rpcGet(false);
+        if (this.activeSession.dataVisibility == 'root') {
+            this.treeService.rpcGet(this.activeSession, false);
+        } else {
+            this.treeService.rpcGet(this.activeSession, true);
         }
     }
 
@@ -52,11 +170,6 @@
         this.sessionsService.storeData();
     }
 
-    setDataVisibility(value: string) {
-        this.activeSession.dataVisibility = value;
-        this.sessionsService.storeData();
-    }
-
     invertStatus() {
         this.activeSession.statusVisibility = !this.activeSession.statusVisibility;
         this.sessionsService.storeData();
@@ -133,43 +246,6 @@
         return version;
     }
 
-    rpcGet(all: boolean) {
-        if (this.activeSession.data) {
-            if ((all && this.activeSession.dataVisibility == 'all') ||
-                (!all && this.activeSession.dataVisibility == 'root')) {
-                return;
-            }
-        }
-        this.loading = true;
-        this.sessionsService.rpcGetSubtree(this.activeSession.key, all).subscribe(result => {
-            if (result['success']) {
-                for (let iter of result['data']) {
-                    this.modsService.setDirty(this.activeSession, iter);
-                }
-                this.activeSession.data = {};
-                this.activeSession.data['path'] = '/';
-                this.activeSession.data['info'] = {};
-                this.activeSession.data['info']['path'] = '/';
-                this.activeSession.data['children'] = result['data'];
-                if (all) {
-                    this.activeSession.dataVisibility = 'all';
-                } else {
-                    this.activeSession.dataVisibility = 'root';
-                }
-                console.log(this.activeSession.data);
-            } else {
-                this.activeSession.dataVisibility = 'none';
-                if ('error-msg' in result) {
-                    this.err_msg = result['error-msg'];
-                } else {
-                    this.err_msg = result['error'][0]['message'];
-                }
-            }
-            this.sessionsService.storeData();
-            this.loading = false;
-        });
-    }
-
     cancelChanges() {
         //console.log(JSON.stringify(this.activeSession.modifications))
         this.modsService.cancelModification(this.activeSession);
@@ -185,6 +261,9 @@
     ngOnInit(): void {
         this.sessionsService.checkSessions();
         this.activeSession = this.sessionsService.getActiveSession();
+        if (!this.activeSession.data) {
+            this.treeService.rpcGet(this.activeSession, false);
+        }
     }
 
     changeActiveSession(key: string) {
diff --git a/frontend/config/modifications.service.ts b/frontend/config/modifications.service.ts
index a0de997..14a3b05 100644
--- a/frontend/config/modifications.service.ts
+++ b/frontend/config/modifications.service.ts
@@ -4,7 +4,7 @@
 import { SessionsService } from './sessions.service';
 
 @Injectable()
-export class ModificationsService{
+export class ModificationsService {
 
     constructor(private sessionsService: SessionsService) {}
 
diff --git a/frontend/config/sessions.service.ts b/frontend/config/sessions.service.ts
index 3d3549b..7487ac7 100644
--- a/frontend/config/sessions.service.ts
+++ b/frontend/config/sessions.service.ts
@@ -92,6 +92,21 @@
             .catch((err: Response | any) => Observable.throw(err));
     }
 
+    private filterSchemas(node, schemas) {
+        if (node['deleted'] || (node['info']['type'] & 0x18)) {
+            /* ignore deleted nodes and nodes that can be instantiated multiple times */
+            return;
+        }
+        for (let index in schemas) {
+            if (!schemas[index]['config'] ||
+                    (schemas[index]['name'] == node['info']['name'] && schemas[index]['module'] == node['info']['module'])) {
+                /* 1. read-only node */
+                /* 2. the node is already instantiated */
+                schemas.splice(index, 1);
+            }
+        }
+    }
+
     childrenSchemas(key: string, path: string, node = null) {
         let params = new URLSearchParams();
         params.set('key', key);
@@ -103,19 +118,14 @@
                 let result = resp.json();
                 console.log(result)
                 if (result['success'] && node) {
-                    for (let iter of node['children']) {
-                        if (iter['deleted'] || (iter['info']['type'] & 0x18)) {
-                            /* ignore deleted nodes and nodes that can be instantiated multiple times */
-                            continue;
+                    if ('children' in node) {
+                        for (let iter of node['children']) {
+                            this.filterSchemas(iter, result['data']);
                         }
-                        for (let index in result['data']) {
-                            if (!result['data'][index]['config'] ||
-                                    (result['data'][index]['name'] == iter['info']['name'] && result['data'][index]['module'] == iter['info']['module'])) {
-                                /* 1. read-only node */
-                                /* 2. the node is already instantiated */
-                                result['data'].splice(index, 1);
-                                break;
-                            }
+                    }
+                    if ('newChildren' in node) {
+                        for (let iter of node['newChildren']) {
+                            this.filterSchemas(iter, result['data']);
                         }
                     }
                 }
diff --git a/frontend/config/tree.component.html b/frontend/config/tree.component.html
index 1c42f86..451452a 100644
--- a/frontend/config/tree.component.html
+++ b/frontend/config/tree.component.html
@@ -2,6 +2,14 @@
     <!-- recursion - show children -->
     <div class="node">
         <tree-indent [node]="node" [indentation]="indentation" [type]="'root'"></tree-indent>
+        <img *ngIf="activeSession.dataVisibility!='all'" class="icon_action" (click)="treeService.rpcGet(activeSession, true)"
+            src="assets/netopeer/icons/show_all.svg" alt="w" title="expand all"
+            onmouseover="this.src='assets/netopeer/icons/show_all_active.svg'"
+            onmouseout="this.src='assets/netopeer/icons/show_all.svg'"/>
+        <img *ngIf="activeSession.dataVisibility!='root'" class="icon_action" (click)="treeService.collapse(activeSession)"
+            src="assets/netopeer/icons/collapse.svg" alt="x" title="collapse"
+            onmouseover="this.src='assets/netopeer/icons/collapse_active.svg'"
+            onmouseout="this.src='assets/netopeer/icons/collapse.svg'"/>
     </div>
     <div class="children" *ngIf="node['children'] || node['newChildren']">
         <ng-container *ngFor="let child of node['children']">
@@ -41,16 +49,16 @@
 		</ng-container>
 
 		<!-- container and lists -->
-		<ng-container *ngIf="!node['new'] && !node['deleted'] && ((node['info']['type'] == 16 || node['info']['type'] == 1)) && expandable(node)">
-            <img *ngIf="hasHiddenChild(node)" (click)="expand(node, true)"
+		<ng-container *ngIf="!node['new'] && !node['deleted'] && ((node['info']['type'] == 16 || node['info']['type'] == 1)) && treeService.expandable(node)">
+            <img *ngIf="treeService.hasHiddenChild(node)" (click)="treeService.expand(activeSession, node, true)"
                 class="icon_action" src="assets/netopeer/icons/show_all.svg"
                 onmouseover="this.src='assets/netopeer/icons/show_all_active.svg'"
                 onmouseout="this.src='assets/netopeer/icons/show_all.svg'" alt="show-all" title="expand subtree"/>
-            <img *ngIf="!node['children']" (click)="expand(node, false)"
+            <img *ngIf="!node['children']" (click)="treeService.expand(activeSession, node, false)"
                 class="icon_action" src="assets/netopeer/icons/show_children.svg"
                 onmouseover="this.src='assets/netopeer/icons/show_children_active.svg'"
                 onmouseout="this.src='assets/netopeer/icons/show_children.svg'" alt="show-children" title="expand children"/>
-            <img *ngIf="node['children']" (click)="collapse(node)"
+            <img *ngIf="node['children']" (click)="treeService.collapse(activeSession, node)"
                 class="icon_action" src="assets/netopeer/icons/collapse.svg"  alt="collapse" title="collapse"
                 onmouseover="this.src='assets/netopeer/icons/collapse_active.svg'"
                 onmouseout="this.src='assets/netopeer/icons/collapse.svg'"/>
diff --git a/frontend/config/tree.component.ts b/frontend/config/tree.component.ts
index 5c79843..2e3ad09 100644
--- a/frontend/config/tree.component.ts
+++ b/frontend/config/tree.component.ts
@@ -4,6 +4,7 @@
 import {Session} from './session';
 import {ModificationsService} from './modifications.service';
 import {SessionsService} from './sessions.service';
+import {TreeService} from './config.component';
 
 @Directive({
     selector: '[treeScrollTo]'
@@ -61,7 +62,7 @@
     creatingDialogSelect(node, index, source) {
         this.modsService.create(this.activeSession, node, index);
         this.sessionsService.storeData();
-        if (node['schemaChildren'].length) {
+        if (('schemaChildren' in node) && node['schemaChildren'].length) {
             source.selectedIndex = 0;
         }
     }
@@ -138,8 +139,10 @@
     @Input() node;
     @Input() indentation;
     activeSession: Session;
+
     constructor(private modsService: ModificationsService,
                 private sessionsService: SessionsService,
+                private treeService: TreeService,
                 private changeDetector: ChangeDetectorRef) {}
 
     ngOnInit(): void {
@@ -220,63 +223,6 @@
         this.sessionsService.storeData();
     }
 
-    expandable(node): boolean {
-        if (node['info']['type'] == 1 || /* container */
-            node['info']['type'] == 16) { /* list */
-                return true;
-        }
-        return false;
-    }
-
-    hasHiddenChild(node, clean=false): boolean {
-        if (!clean && 'hasHiddenChild' in node) {
-            return node['hasHiddenChild'];
-        }
-        node['hasHiddenChild'] = false;
-        if (!this.expandable(node)) {
-            /* terminal node (leaf or leaf-list) */
-            return node['hasHiddenChild'];
-        } else if (!('children' in node)) {
-            /* internal node without children */
-            node['hasHiddenChild'] = true;
-        } else {
-            /* go recursively */
-            for (let child of node['children']) {
-                if (this.hasHiddenChild(child, clean)) {
-                    node['hasHiddenChild'] = true;
-                    break;
-                }
-            }
-        }
-        return node['hasHiddenChild'];
-    }
-
-    collapse(node) {
-        delete node['children'];
-        this.activeSession.dataVisibility = 'mixed';
-        for (let iter of this.activeSession.data) {
-            this.hasHiddenChild(iter, true);
-        }
-        this.sessionsService.storeData();
-    }
-
-    expand(node, all: boolean) {
-        node['loading'] = true;
-        this.sessionsService.rpcGetSubtree(this.activeSession.key, all, node['path']).subscribe(result => {
-            if (result['success']) {
-                for (let iter of result['data']['children']) {
-                    this.modsService.setDirty(this.activeSession, iter);
-                }
-                node['children'] = result['data']['children'];
-                for (let iter of this.activeSession.data) {
-                    this.hasHiddenChild(iter, true);
-                }
-                delete node['loading'];
-                this.sessionsService.storeData();
-            }
-        });
-    }
-
     newChildrenToShow(node) {
         if ('newChildren' in node) {
             return node['newChildren'];