gui CHANGE rewrite list and leaf-list manipulation

To prepare for ordering, group list and leaf-list instances together
when displaying (providing them from backend) and also in manipulating
(creating, deleting in frontend)
diff --git a/frontend/config/config.component.html b/frontend/config/config.component.html
index 1918263..9645b70 100644
--- a/frontend/config/config.component.html
+++ b/frontend/config/config.component.html
@@ -49,7 +49,7 @@
             <th class="item_right" >Data</th>
         </tr>
     </table>
-    <div class="loading" *ngIf="treeService.loading">
+    <div class="loading" *ngIf="activeSession.loading">
         <span>Retrieving data ...</span>
         <div><netopeer-loading spinner="true"></netopeer-loading></div>
     </div>
@@ -62,8 +62,8 @@
         <tree-view [node]="activeSession.data"></tree-view>
         <!--  <pre *ngIf="activeSession.data['schemaChildren']">{{activeSession.data['schemaChildren'] | json}}</pre> -->
     </div>
-	<div #modificationsStatus *ngIf="activeSession.modifications" class="modifications-status">&nbsp;
-		<div *ngIf="activeSession.modifications" class="msg-rounded" [style.width.px]="modificationsStatus.offsetWidth">
+    <div #modificationsStatus *ngIf="activeSession.modifications" class="modifications-status">&nbsp;
+        <div *ngIf="activeSession.modifications" class="msg-rounded" [style.width.px]="modificationsStatus.offsetWidth">
             <div>Configuration data were changed. Do you wish to
                 <button (click)="applyChanges()">apply</button> /
                 <button (click)="cancelChanges()">cancel</button>
@@ -72,8 +72,8 @@
             <ng-container *ngFor="let err of commit_error; let i = index">
                 <div class="msg-rounded msg-failure"><span class="msg-close" (click)="commit_error.splice(i,1)">x</span>{{err['message']}}</div>
             </ng-container>
-		</div>
-	</div>
+        </div>
+    </div>
     </ng-container>
 </div>
 
diff --git a/frontend/config/config.component.ts b/frontend/config/config.component.ts
index c31acfd..f57db9a 100644
--- a/frontend/config/config.component.ts
+++ b/frontend/config/config.component.ts
@@ -1,131 +1,16 @@
-import {Component, Injectable, OnInit} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
 import {Router} from '@angular/router';
 
+import {TreeService} from './tree.service';
 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';
-                }
-            }
-            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'],
-    providers: [ModificationsService, TreeService]
+    providers: [ModificationsService]
 })
 
 export class ConfigComponent implements OnInit {
@@ -136,7 +21,6 @@
 
     constructor(private sessionsService: SessionsService,
                 private modsService: ModificationsService,
-                private treeService: TreeService,
                 private router: Router) {}
 
     addSession() {
@@ -146,9 +30,9 @@
     reloadData() {
         this.activeSession.data = null;
         if (this.activeSession.dataVisibility == 'root') {
-            this.treeService.rpcGet(this.activeSession, false);
+            this.sessionsService.rpcGet(this.activeSession, false);
         } else {
-            this.treeService.rpcGet(this.activeSession, true);
+            this.sessionsService.rpcGet(this.activeSession, true);
         }
     }
 
@@ -255,6 +139,7 @@
     }
 
     applyChanges() {
+        //console.log(JSON.stringify(this.activeSession.modifications))
         this.modsService.applyModification(this.activeSession).then(result => {
             if (result['success']) {
                 this.reloadData();
@@ -269,7 +154,7 @@
         this.sessionsService.checkSessions();
         this.activeSession = this.sessionsService.getActiveSession();
         if (this.activeSession && !this.activeSession.data) {
-            this.treeService.rpcGet(this.activeSession, false);
+            this.sessionsService.rpcGet(this.activeSession, false);
         }
     }
 
diff --git a/frontend/config/modifications.service.ts b/frontend/config/modifications.service.ts
index e553df8..eb95d2b 100644
--- a/frontend/config/modifications.service.ts
+++ b/frontend/config/modifications.service.ts
@@ -2,11 +2,12 @@
 
 import { Session} from './session';
 import { SessionsService } from './sessions.service';
+import { TreeService } from './tree.service';
 
 @Injectable()
 export class ModificationsService {
 
-    constructor(private sessionsService: SessionsService) {}
+    constructor(private sessionsService: SessionsService, private treeService: TreeService) {}
 
     createModificationsRecord(activeSession, path) {
         if (!activeSession.modifications) {
@@ -44,23 +45,11 @@
         }
     }
 
-    setDirty(activeSession, node) {
-        if (!activeSession.modifications) {
-            return;
-        }
-
-        if (node['path'] in activeSession.modifications) {
-            node['dirty'] = true;
-            if (activeSession.modifications[node['path']]['type'] == 'change') {
-                activeSession.modifications[node['path']]['original'] = node['value'];
-            }
-            node['value'] = activeSession.modifications[node['path']]['value']; 
-        }
-        /* recursion */
-        if ('children' in node) {
-            for (let child of node['children']) {
-                this.setDirty(activeSession, child);
-            }
+    renameModificationsRecord(activeSession, oldPath, newPath) {
+        let record = this.getModificationsRecord(activeSession, oldPath);
+        if (record) {
+            activeSession.modifications[newPath] = record;
+            delete activeSession.modifications[oldPath];
         }
     }
 
@@ -101,76 +90,6 @@
         last['last'] = true;
     }
 
-    nodeParent(activeSession, node) {
-        if (node['path'] =='/') {
-            return null;
-        }
-
-        let match = false;
-        let parent = null;
-        let children = activeSession.data['children'];
-        let newChildren = activeSession.data['newChildren'];
-
-        while (children || newChildren) {
-            match = false;
-
-            if (children) {
-                for (let iter of children) {
-                    if (node['path'] == iter['path']) {
-                        match = true;
-                        children = null;
-                        newChildren = null;
-                        break;
-                    } else if (node['path'].startsWith(iter['path'] + '/')) {
-                        match = true;
-                        parent = iter;
-                        children = iter['children'];
-                        if (('new' in node) && ('newChildren' in iter)) {
-                            newChildren = iter['newChildren'];
-                        } else {
-                            newChildren = null;
-                        }
-                        break;
-                    }
-                }
-                if (!match) {
-                    children = null;
-                }
-            }
-            if (match) {
-                continue;
-            }
-            if (newChildren) {
-                for (let iter of newChildren) {
-                    if (node['path'] == iter['path']) {
-                        match = true;
-                        children = null;
-                        newChildren = null;
-                        break;
-                    } else if (node['path'].startsWith(iter['path'] + '/')) {
-                        match = true;
-                        parent = iter;
-                        children = iter['children'];
-                        if (('new' in node) && ('newChildren' in iter)) {
-                            newChildren = iter['newChildren'];
-                        } else {
-                            newChildren = null;
-                        }
-                        break;
-                    }
-                }
-                if (!match) {
-                    children = null;
-                }
-            }
-        }
-
-        if (!parent) {
-            parent = activeSession.data;
-        }
-        return parent;
-    }
-
     schemaName(parent, child):string {
         if (parent['module'] != child['module']) {
             return child['module'] + ':' + child['name'];
@@ -179,46 +98,39 @@
         }
     }
 
-    isDeleted(node, value = null): boolean {
+    isDeleted(node): boolean {
         if ('deleted' in node) {
-            if (typeof node['deleted'] === 'boolean') {
-                return node['deleted'];
-            } else if (value) {
-                if (node['deleted'].indexOf(value) != -1) {
-                    return true;
-                }
-            }
+            return node['deleted'];
         }
         return false;
     }
 
-    delete(activeSession, node, value = null) {
+    private deleteChild(activeSession, parent, childArray, node) {
+        for (let i in parent[childArray]) {
+            if (parent[childArray][i]['path'] == node['path']) {
+                parent[childArray].splice(i, 1);
+                break;
+            }
+        }
+        if (childArray != 'children' && !parent[childArray].length) {
+            delete parent[childArray];
+        }
+    }
+
+    delete(activeSession, node) {
         if ('new' in node) {
             /* removing newly created subtree */
-            let parent = this.nodeParent(activeSession, node);
+            let parent = this.treeService.nodeParent(activeSession, node);
             if ('new' in parent) {
                 /* removing just a subtree of the created tree */
-                for (let i in parent['children']) {
-                    if (parent['children'][i] == node) {
-                        parent['children'].splice(i, 1);
-                        break;
-                    }
-                }
+                this.deleteChild(activeSession, parent, 'children', node);
             } else {
                 this.removeModificationsRecord(activeSession, node['path']);
-                for (let i in parent['newChildren']) {
-                    if (parent['newChildren'][i]['path'] == node['path']) {
-                        parent['newChildren'].splice(i, 1);
-                        break;
-                    }
-                }
-                if (!parent['newChildren'].length) {
-                    delete parent['newChildren'];
-                }
+                this.deleteChild(activeSession, parent, 'newChildren', node);
             }
         } else {
             let record = this.createModificationsRecord(activeSession, node['path']);
-
+            node['deleted'] = true;
             if (!('type' in record)) {
                 /* new record */
                 record['type'] = 'delete';
@@ -230,24 +142,14 @@
                 delete record['original'];
                 delete record['value'];
             }
-            if (value) {
-                if (!('deleted' in node) || typeof node['deleted'] === 'boolean') {
-                    node['deleted'] = [];
-                }
-                node['deleted'].push(value);
-            } else if (node['info']['type'] == 8) {
-                node['deleted'] = node['value'].slice(0);
-            } else {
-                node['deleted'] = true;
-            }
         }
     }
 
     change(activeSession, node, leafValue) {
+        let record = null;
         if (!('new' in node)) {
-            let record = this.createModificationsRecord(activeSession, node['path']);
+            record = this.createModificationsRecord(activeSession, node['path']);
             if (!('type' in record)) {
-                console.log(record);
                 /* new record */
                 if (node['value'] == leafValue) {
                     /* no change to the original value */
@@ -260,43 +162,71 @@
                 record['value'] = leafValue;
                 node['dirty'] = true;
             } else if (record['type'] == 'change' && record['original'] == leafValue) {
-                console.log(record);
                 /* change back to the original value, remove the change record */
                 this.removeModificationsRecord(activeSession, node['path']);
                 node['dirty'] = false;
             } else {
-                console.log(record);
                 /* another change of existing change record */
                 record['value'] = leafValue;
                 node['dirty'] = true;
             }
+        } else if (node['info']['type'] == 8) {
+            record = this.getModificationsRecord(activeSession, node['path']);
+            let newPath = node['path'].slice(0, node['path'].lastIndexOf('[')) + '[.=\'' + leafValue + '\']';
+            this.renameModificationsRecord(activeSession, node['path'], newPath);
+            node['path'] = newPath
         }
 
         node['value'] = leafValue;
         this.setEdit(activeSession, node, false);
     }
 
-    createOpen(schemas, node) {
+    createOpen(activeSession, schemas, node) {
         //console.trace();
         node['schemaChildren'] = schemas;
         node['creatingChild'] = {};
 
         if (schemas.length) {
-            if (('newChildren' in node) && node['newChildren'].length) {
-                delete node['newChildren'][node['newChildren'].length - 1]['last']
-            } else if (('children' in node) && node['children'].length) {
-                delete node['children'][node['children'].length - 1]['last'];
+            let children = this.treeService.childrenToShow(node);
+            console.log(children)
+            if (children.length) {
+                let last = children[children.length - 1];
+                if (last['info']['type'] == 16) {
+                    let instances = this.treeService.getInstances(activeSession, last)
+                    last = instances[instances.length - 1];
+                }
+                delete last['last'];
+                if (last['info']['type'] == 8) {
+                    for (let sibling of this.treeService.getInstances(activeSession, last)) {
+                        if ('last' in sibling) {
+                            continue;
+                        }
+                        delete sibling["lastLeafList"];
+                    }
+                }
             }
         }
     }
 
-    createClose(node, reason='abort') {
+    createClose(activeSession, node, reason='abort') {
         //console.trace();
         if (reason == 'abort' && node['schemaChildren'].length) {
-            if (('newChildren' in node) && node['newChildren'].length) {
-                node['newChildren'][node['newChildren'].length - 1]['last'] = true;
-            } else if (('children' in node) && node['children'].length) {
-                node['children'][node['children'].length - 1]['last'] = true;
+            let children = this.treeService.childrenToShow(node);
+            if (children.length) {
+                let last = children[children.length - 1];
+                if (last['info']['type'] == 16) {
+                    let instances = this.treeService.getInstances(activeSession, last)
+                    last = instances[instances.length - 1];
+                }
+                last['last'] = true;
+                if (last['info']['type'] == 8) {
+                    for (let sibling of this.treeService.getInstances(activeSession, last)) {
+                        if ('last' in sibling) {
+                            continue;
+                        }
+                        sibling["lastLeafList"] = true;
+                    }
+                }
             }
         }
         delete node['creatingChild'];
@@ -338,7 +268,7 @@
 
         if ('new' in node) {
             if (!('children' in node)) {
-                node['children'] = []
+                node['children'] = [];
             }
             node['children'].push(newNode)
         } else {
@@ -355,7 +285,7 @@
             newNode['children'] = [];
             /* open creation dialog for nodes inside the created container */
             this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
-                this.createOpen(result, newNode);
+                this.createOpen(activeSession, result, newNode);
             });
             break;
         case 4: /* leaf */
@@ -367,6 +297,16 @@
             this.setEdit(activeSession, newNode, true)
             break;
         case 8: /* leaf-list */
+            /* find the first instance, if not, mark this as the first leaf-list instance */
+            for (let sibling of this.treeService.childrenToShow(node)) {
+                if (sibling == newNode) {
+                    newNode['first'] = true;
+                    break;
+                }
+                if (sibling['info']['name'] == newNode['info']['name'] && sibling['info']['module'] == newNode['info']['module']) {
+                    break;
+                }
+            }
             newNode['path'] = newNode['path'] + '[' + this.list_nextpos(node, newNode['path']) + ']';
             this.setEdit(activeSession, newNode, true)
             console.log(newNode);
@@ -377,7 +317,7 @@
             /* open creation dialog for nodes inside the created list */
             this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
                 if (result && result.length) {
-                    this.createOpen(result, newNode);
+                    this.createOpen(activeSession, result, newNode);
                 }
 
                 if (newNode['schemaChildren'].length) {
@@ -395,7 +335,8 @@
                         newNode['schemaChildren'].splice(i, 1);
                         if (!newNode['schemaChildren'].length) {
                             newKey['last'] = true;
-                            this.createClose(newNode, 'success');
+                            this.createClose(activeSession, newNode, 'success');
+                            console.log(JSON.stringify(newNode));
                         }
                     }
                 }
@@ -406,7 +347,7 @@
 
         if (!node['schemaChildren'].length) {
             newNode['last'] = true;
-            this.createClose(node, 'success');
+            this.createClose(activeSession, node, 'success');
         }
 
         if (!('new' in node)) {
@@ -440,7 +381,7 @@
 
         if ('new' in node) {
             /* removing newly created subtree */
-            let parent = this.nodeParent(activeSession, node);
+            let parent = this.treeService.nodeParent(activeSession, node);
             if ('new' in parent) {
                 /* removing just a subtree of the created tree */
                 for (let i in parent['children']) {
@@ -477,7 +418,7 @@
                     schemas = parent['schemaChildren'];
                 }
                 schemas.push(node['info']);
-                this.createOpen(schemas, parent)
+                this.createOpen(activeSession, schemas, parent)
             }
         } else if (activeSession.modifications) {
             let record = this.getModificationsRecord(activeSession, node['path']);
diff --git a/frontend/config/session.ts b/frontend/config/session.ts
index 5bdda36..09a3050 100644
--- a/frontend/config/session.ts
+++ b/frontend/config/session.ts
@@ -4,6 +4,7 @@
   constructor (
     public key: string,
     public device: Device,
+    public loading = false,
     public data = null,
     public modifications = null,
     public cpblts: string = "",
diff --git a/frontend/config/sessions.service.ts b/frontend/config/sessions.service.ts
index 1c6b5cd..d3b25d0 100644
--- a/frontend/config/sessions.service.ts
+++ b/frontend/config/sessions.service.ts
@@ -5,15 +5,16 @@
 import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/do';
 
+import { TreeService } from './tree.service';
 import { Device } from '../inventory/device';
 import { Session } from './session';
 
 @Injectable()
 export class SessionsService implements OnInit {
     public sessions: Session[];
-    public activeSession;
+    public activeSession: string;
 
-    constructor(private http: Http) {
+    constructor(private http: Http, private treeService: TreeService) {
         this.activeSession = localStorage.getItem('activeSession');
         if (!this.activeSession) {
             this.activeSession = "";
@@ -85,6 +86,35 @@
         }
     }
 
+    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.treeService.updateHiddenFlags( activeSession );
+        this.storeData();
+    }
+
+    expand( activeSession, node, all: boolean ) {
+        node['loading'] = true;
+        this.rpcGetSubtree( activeSession.key, all, node['path'] ).subscribe( result => {
+            if ( result['success'] ) {
+                for ( let iter of result['data']['children'] ) {
+                    this.treeService.setDirty( activeSession, iter );
+                }
+                node['children'] = result['data']['children'];
+                this.treeService.updateHiddenFlags( activeSession );
+                delete node['loading'];
+                this.storeData();
+            }
+        } );
+    }
+
     checkValue(key: string, path: string, value: string): Observable<string[]> {
         let params = new URLSearchParams();
         params.set('key', key);
@@ -203,6 +233,35 @@
             })
             .catch((err: Response | any) => Observable.throw(err));
     }
+    rpcGet( activeSession: Session, all: boolean ) {
+        if ( activeSession.data ) {
+            if ( ( all && activeSession.dataVisibility == 'all' ) ||
+                ( !all && activeSession.dataVisibility == 'root' ) ) {
+                return;
+            }
+        }
+        activeSession.loading = true;
+        delete activeSession.data;
+        this.rpcGetSubtree( activeSession.key, all ).subscribe( result => {
+            if ( result['success'] ) {
+                for ( let iter of result['data'] ) {
+                    this.treeService.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';
+                }
+            }
+            activeSession.loading = false;
+            this.storeData();
+        } );
+    }
 
     commit(key: string) {
         let activeSession = this.getActiveSession(key);
diff --git a/frontend/config/tree-indent.html b/frontend/config/tree-indent.html
index 0c0978d..7bfe142 100644
--- a/frontend/config/tree-indent.html
+++ b/frontend/config/tree-indent.html
@@ -15,12 +15,12 @@
             (click)="cancelModification(node)"/>
     </ng-container>
     <ng-container *ngIf="getType() == 'value'">
-        <ng-container [ngSwitch]="modsService.isDeleted(node, value)">
+        <ng-container [ngSwitch]="modsService.isDeleted(node)">
             <img *ngSwitchCase="false" title="delete value"
                 class="icon_action" src="assets/netopeer/icons/close.svg"
                 onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
                 onmouseout="this.src='assets/netopeer/icons/close.svg'"
-                (click)="deleteSubtree(node, value)"/>
+                (click)="deleteInstance(node)"/>
             <img *ngSwitchCase="true" title="cancel modification"
                 class="icon_action" src="assets/netopeer/icons/close.svg"
                 onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
@@ -65,7 +65,7 @@
     <img *ngIf="indent" class="indentation" src="assets/netopeer/icons/tree_empty.svg" />
 </ng-container>
 <ng-container [ngSwitch]="getType()">
-    <img *ngSwitchCase="'value'" class="indentation" src="assets/netopeer/icons/tree_empty.svg" />
+    <img *ngSwitchCase="'value'" class="indentation value" src="assets/netopeer/icons/tree_empty.svg" />
     <img *ngSwitchCase="'root'" class="indentation" src="assets/netopeer/icons/tree_root.svg" />
     <img *ngSwitchCase="'edit'" class="indentation" src="assets/netopeer/icons/tree_empty.svg" />
     <img *ngSwitchCase="'create'" class="indentation" src="assets/netopeer/icons/tree_last_branch.svg" />
diff --git a/frontend/config/tree-node.html b/frontend/config/tree-node.html
new file mode 100644
index 0000000..6cefbcc
--- /dev/null
+++ b/frontend/config/tree-node.html
@@ -0,0 +1,120 @@
+<div *ngIf="node['info']['type'] != 8 || node['first']" class="node" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node)"
+    [class.yang-container]="node['info']['type'] == 1"
+    [class.yang-leaf]="node['info']['type'] == 4"
+    [class.yang-leaflist]="node['info']['type'] == 8"
+    [class.yang-list]="node['info']['type'] == 16">
+    <tree-indent [node]="node" [indentation]="indentation"></tree-indent>
+    <img class="icon" src="assets/netopeer/icons/info.svg" alt="info" title="{{node['info']['dsc']}}"
+        onmouseover="this.src='assets/netopeer/icons/info_active.svg'"
+        onmouseout="this.src='assets/netopeer/icons/info.svg'"/>
+
+    <!-- BEGIN nodetype-specific code -->
+    <!-- leaf -->
+    <ng-container *ngIf="node['info']['type'] == 4 || (node['new'] && node['info']['type'] == 8)">
+        <ng-container *ngIf="node['info']['key']">
+            <img class="icon" src="assets/netopeer/icons/key.svg" alt="key" title="list key"/>
+        </ng-container>
+        <ng-container *ngIf="node['info']['config'] && !node['deleted'] && (!node['info']['key'] || node['new']) && node['info']['datatypebase'] != 'empty'">
+            <img *ngIf="!node['edit']" class="icon_action" src="assets/netopeer/icons/edit.svg"
+                alt="edit" title="edit value" tabindex=0
+                (click)="startEditing(node, $event.target);" (keyup.enter)="startEditing(node, $event.target);"
+                onmouseover="this.src='assets/netopeer/icons/edit_active.svg'"
+                onmouseout="this.src='assets/netopeer/icons/edit.svg'"/>
+            <img *ngIf="node['edit']" class="icon" src="assets/netopeer/icons/edit.svg" alt="edit" title="editing value"/>
+        </ng-container>
+    </ng-container>
+
+    <!-- container and lists -->
+    <ng-container *ngIf="!node['new'] && !node['deleted'] && ((node['info']['type'] == 16 || node['info']['type'] == 1)) && treeService.expandable(node)">
+        <img *ngIf="treeService.hasHiddenChild(node)" (click)="sessionsService.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)="sessionsService.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)="sessionsService.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'"/>
+    </ng-container>
+
+    <div class="node_info"><span class="node_name">{{node['info']['name']}}</span>
+        <!-- list's keys -->
+        <span *ngIf="node['info']['type'] == 16" class="keys">* [{{node['keys']}}]</span>
+
+        <!-- leaf and leaflists -->
+        <span *ngIf="node['info']['type'] == 8 || node['info']['type'] == 4">:</span>
+
+        <!-- leaf -->
+        <ng-container *ngIf="node['info']['type'] == 4">
+            <span class="value_inline" *ngIf="!node['edit']" (click)="startEditing(node, $event.target);">{{node['value']}}</span>
+            <span class="value_inline" *ngIf="node['edit']">{{node['info']['datatype']}}
+                <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
+            </span>
+        </ng-container>
+        <ng-container *ngIf="node['info']['type'] == 8">
+            <span class="value_inline">{{node['info']['datatype']}}
+                <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
+            </span>
+        </ng-container>
+    </div>
+    <!-- END nodetype-specific code -->
+
+    <div class="module_name" (click)="showSchema(node)" title="open schema {{node['info']['module']}} in YANG Explorer">{{moduleName(node)}}</div>
+</div>
+
+<!-- BEGIN nodetype-specific code -->
+<!-- leaflist's values -->
+<ng-container *ngIf="node['info']['type'] == 8">
+    <div class="node yang-leaflist-value" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node)">
+        <tree-indent [node]="node" [indentation]="treeService.inheritIndentation(indentation, node)" [type]="'value'" [value]="value"></tree-indent>
+        <div class="value_standalone">{{node['value']}}</div>
+    </div>
+</ng-container>
+
+<!-- leaf/leaf-list's edit value -->
+<ng-container *ngIf="node['info']['type'] == 4 || node['info']['type'] == 8">
+    <div class="node_edit" [class.dirty]="node['dirty']" *ngIf="node['edit']">
+        <tree-indent [node]="node" [indentation]="treeService.inheritIndentation(indentation, node)" [type]="'edit'"></tree-indent>
+        <img *ngIf="node['edit'] " class="icon_action" src="assets/netopeer/icons/close.svg"
+            alt="cancel" title="cancel editing" tabindex=0
+            (click)="changeValueCancel(node)" (keyup.enter)="changeValueCancel(node)"
+            onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
+            onmouseout="this.src='assets/netopeer/icons/close.svg'" />
+        <img *ngIf="node['edit']" class="icon_action icon_hidden" src="assets/netopeer/icons/confirm.svg"
+            id="{{node['path']}}_value_confirm" alt="done" title="store changes" tabindex=0
+            (click)="changeValue(node, $event.target)" (keyup.enter)="changeValue(node, $event.target)"
+            onmouseover="this.src='assets/netopeer/icons/confirm_active.svg'"
+            onmouseout="this.src='assets/netopeer/icons/confirm.svg'"/>
+        <select *ngIf="node['info']['values']"
+            id="{{node['path']}}_input" type="text" class="value_inline"
+            (change)="checkValue(node, $event.target, true)"
+            checkLeafValue [node]="node" [trusted]="true" (onCheckValue)="checkValue($event.node, $event.element, $event.trusted)">
+            <ng-container *ngFor="let value of node['info']['values']; let i='index'">
+                <option value="{{value}}">{{value}}</option>
+            </ng-container>
+        </select>
+        <input *ngIf="!node['info']['values']"
+            id="{{node['path']}}_input" type="text" class="value_inline" value="{{nodeValue(node)}}" tabindex=0
+            (keyup)="checkValue(node, $event.target)" (change)="checkValue(node, $event.target)"
+            (keyup.enter)="changeValue(node, $event.target)" (keyup.escape)="changeValueCancel(node)"
+            checkLeafValue [node]="node" (onCheckValue)="checkValue($event.node, $event.element)"/>
+    </div>
+</ng-container>
+
+<!-- END nodetype-specific code -->
+
+<!-- recursion - show children -->
+<div class="loading" *ngIf="node['loading']">
+    <netopeer-loading></netopeer-loading>
+</div>
+<div class="children" *ngIf="(node['children'] || node['newChildren']) && !node['deleted']">
+    <ng-container *ngFor="let child of treeService.childrenToShow(node)">
+        <tree-view [node]="child" [indentation]="treeService.inheritIndentation(indentation, node)"></tree-view>
+    </ng-container>
+</div>
+
+<!-- create new child dialog -->
+<tree-create [node]="node" [indentation]="treeService.inheritIndentation(indentation, node)"></tree-create>
diff --git a/frontend/config/tree.component.html b/frontend/config/tree.component.html
index f2415ab..b4a5492 100644
--- a/frontend/config/tree.component.html
+++ b/frontend/config/tree.component.html
@@ -2,20 +2,17 @@
     <!-- 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)"
+        <img *ngIf="activeSession.dataVisibility!='all'" class="icon_action" (click)="sessionsService.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)"
+        <img *ngIf="activeSession.dataVisibility!='root'" class="icon_action" (click)="sessionsService.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']">
-            <tree-view [node]="child"></tree-view>
-        </ng-container>
-        <ng-container *ngFor="let child of newChildrenToShow(node)">
+        <ng-container *ngFor="let child of treeService.childrenToShow(node)">
             <tree-view [node]="child"></tree-view>
         </ng-container>
     </div>
@@ -26,127 +23,7 @@
 <div class="subtree" *ngIf="node['path'] != '/' && (node['info']['config'] || activeSession.statusVisibility)"
     [class.status]="!node['info']['config']">
 
-    <div class="node" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node)"
-            [class.yang-container]="node['info']['type'] == 1"
-            [class.yang-leaf]="node['info']['type'] == 4"
-            [class.yang-leaflist]="node['info']['type'] == 8"
-            [class.yang-list]="node['info']['type'] == 16">
-		<tree-indent [node]="node" [indentation]="indentation"></tree-indent>
-		<img class="icon" src="assets/netopeer/icons/info.svg" alt="info" title="{{node['info']['dsc']}}"
-			onmouseover="this.src='assets/netopeer/icons/info_active.svg'"
-			onmouseout="this.src='assets/netopeer/icons/info.svg'"/>
-
-		<!-- BEGIN nodetype-specific code -->
-		<!-- leaf -->
-		<ng-container *ngIf="node['info']['type'] == 4 || (node['new'] && node['info']['type'] == 8)">
-			<ng-container *ngIf="node['info']['key']">
-				<img class="icon" src="assets/netopeer/icons/key.svg" alt="key" title="list key"/>
-			</ng-container>
-			<ng-container *ngIf="node['info']['config'] && !node['deleted'] && (!node['info']['key'] || node['new']) && node['info']['datatypebase'] != 'empty'">
-				<img *ngIf="!node['edit']" class="icon_action" src="assets/netopeer/icons/edit.svg"
-					alt="edit" title="edit value" tabindex=0
-					(click)="startEditing(node, $event.target);" (keyup.enter)="startEditing(node, $event.target);"
-					onmouseover="this.src='assets/netopeer/icons/edit_active.svg'"
-					onmouseout="this.src='assets/netopeer/icons/edit.svg'"/>
-				<img *ngIf="node['edit']" class="icon" src="assets/netopeer/icons/edit.svg" alt="edit" title="editing value"/>
-			</ng-container>
-		</ng-container>
-
-        <!-- container and lists -->
-        <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)="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)="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'"/>
-        </ng-container>
-
-        <div class="node_info"><span class="node_name">{{node['info']['name']}}</span>
-            <!-- list's keys -->
-            <span *ngIf="node['info']['type'] == 16" class="keys">* [{{node['keys']}}]</span>
-
-            <!-- leaf and leaflists -->
-            <span *ngIf="node['info']['type'] == 8 || node['info']['type'] == 4">:</span>
-
-            <!-- leaf -->
-            <ng-container *ngIf="node['info']['type'] == 4">
-                <span class="value" *ngIf="!node['edit']" (click)="startEditing(node, $event.target);">{{node['value']}}</span>
-                <span class="value" *ngIf="node['edit']">{{node['info']['datatype']}}
-                    <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
-                </span>
-            </ng-container>
-            <ng-container *ngIf="node['info']['type'] == 8">
-                <span class="value">{{node['info']['datatype']}}
-                    <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
-                </span>
-            </ng-container>
-        </div>
-        <!-- END nodetype-specific code -->
-
-		<div class="module_name" (click)="showSchema(node)" title="open schema {{node['info']['module']}} in YANG Explorer">{{moduleName(node)}}</div>
-	</div>
-
-	<!-- BEGIN nodetype-specific code -->
-	<!-- leaf/leaf-list's edit value -->
-	<ng-container *ngIf="node['info']['type'] == 4 || node['info']['type'] == 8">
-		<div class="node_edit" [class.dirty]="node['dirty']" *ngIf="node['edit']">
-            <tree-indent [node]="node" [indentation]="inheritIndentation(node)" [type]="'edit'"></tree-indent>
-			<img *ngIf="node['edit'] " class="icon_action" src="assets/netopeer/icons/close.svg"
-				alt="cancel" title="cancel editing" tabindex=0
-				(click)="changeValueCancel(node)" (keyup.enter)="changeValueCancel(node)"
-				onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
-				onmouseout="this.src='assets/netopeer/icons/close.svg'" />
-			<img *ngIf="node['edit']" class="icon_action icon_hidden" src="assets/netopeer/icons/confirm.svg"
-				id="{{node['path']}}_value_confirm" alt="done" title="store changes" tabindex=0
-				(click)="changeValue(node, $event.target)" (keyup.enter)="changeValue(node, $event.target)"
-				onmouseover="this.src='assets/netopeer/icons/confirm_active.svg'"
-				onmouseout="this.src='assets/netopeer/icons/confirm.svg'"/>
-            <select *ngIf="node['info']['values']"
-                id="{{node['path']}}_input" type="text" class="value"
-                (change)="checkValue(node, $event.target, true)"
-                checkLeafValue [node]="node" [trusted]="true" (onCheckValue)="checkValue($event.node, $event.element, $event.trusted)">
-                <ng-container *ngFor="let value of node['info']['values']; let i='index'">
-                    <option value="{{value}}">{{value}}</option>
-                </ng-container>
-            </select>
-            <input *ngIf="!node['info']['values']"
-                id="{{node['path']}}_input" type="text" class="value" value="{{nodeValue(node)}}" tabindex=0
-                (keyup)="checkValue(node, $event.target)" (change)="checkValue(node, $event.target)"
-                (keyup.enter)="changeValue(node, $event.target)" (keyup.escape)="changeValueCancel(node)"
-                checkLeafValue [node]="node" (onCheckValue)="checkValue($event.node, $event.element)"/>
-		</div>
-	</ng-container>
-
-    <!-- leaflist's values -->
-    <ng-container *ngIf="node['info']['type'] == 8 && !node['edit'] && !modsService.isDeleted(node)">
-        <div *ngFor="let value of node['value']"
-            class="node yang-leaflist-value" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node, value)">
-            <tree-indent [node]="node" [indentation]="inheritIndentation(node)" [type]="'value'" [value]="value"></tree-indent>
-            <div class="value_standalone">{{value}}</div>
-        </div>
+    <ng-container *ngFor="let item of treeService.nodesToShow(activeSession, node)">
+        <tree-node [activeSession]="activeSession" [node]="item" [indentation]="indentation"></tree-node>
     </ng-container>
-    <!-- END nodetype-specific code -->
-
-	<!-- recursion - show children -->
-    <div class="loading" *ngIf="node['loading']">
-        <netopeer-loading></netopeer-loading>
-    </div>
-    <div class="children" *ngIf="(node['children'] || node['newChildren']) && !node['deleted']">
-        <ng-container *ngFor="let child of node['children']">
-            <tree-view [node]="child" [indentation]="inheritIndentation(node)"></tree-view>
-        </ng-container>
-        <ng-container *ngFor="let child of newChildrenToShow(node)">
-            <tree-view [node]="child" [indentation]="inheritIndentation(node)"></tree-view>
-        </ng-container>
-    </div>
-
-    <!-- create new child dialog -->
-    <tree-create [node]="node" [indentation]="inheritIndentation(node)"></tree-create>
 </div>
diff --git a/frontend/config/tree.component.scss b/frontend/config/tree.component.scss
index 3e8bd4a..8a2ced3 100644
--- a/frontend/config/tree.component.scss
+++ b/frontend/config/tree.component.scss
@@ -108,10 +108,15 @@
     flex-grow: 1;
 }
 
-.value {
+.value_inline {
     margin-left: 1.5em;
 }
 
+.indentation.value {
+    width: 0.8em;
+    height: 100%;
+}
+
 .indentation {
     width: 1.7em;
     height: 100%;
diff --git a/frontend/config/tree.component.ts b/frontend/config/tree.component.ts
index bcb4da4..2810095 100644
--- a/frontend/config/tree.component.ts
+++ b/frontend/config/tree.component.ts
@@ -5,7 +5,7 @@
 import {Schema} from '../inventory/schema';
 import {ModificationsService} from './modifications.service';
 import {SessionsService} from './sessions.service';
-import {TreeService} from './config.component';
+import {TreeService} from './tree.service';
 import {SchemasService} from '../yang/schemas.service';
 
 @Directive({
@@ -60,10 +60,11 @@
     }
 
     closeCreatingDialog(node, reason='abort') {
-        this.modsService.createClose(node, reason);
+        this.modsService.createClose(this.activeSession, node, reason);
     }
 
     creatingDialogSelect(node, index, source) {
+        console.log(node)
         this.modsService.create(this.activeSession, node, index);
         this.sessionsService.storeData();
         if (('schemaChildren' in node) && node['schemaChildren'].length) {
@@ -85,7 +86,9 @@
     activeSession: Session;
     private timeout;
 
-    constructor(private modsService: ModificationsService, private sessionsService: SessionsService) {}
+    constructor(private treeService: TreeService,
+                private modsService: ModificationsService,
+                private sessionsService: SessionsService) {}
 
     ngOnInit(): void {
         this.activeSession = this.sessionsService.getActiveSession();
@@ -119,36 +122,31 @@
         menu.style.visibility = "hidden";
     }
 
-    deleteSubtree(node, value = null) {
-        this.modsService.delete(this.activeSession, node, value);
+    deleteSubtree(node) {
+        let rmlist = [];
+        if (node['info']['type'] == 8) {
+            rmlist = this.treeService.nodesToShow(this.activeSession, node);
+        } else {
+            rmlist.push(node);
+        }
+        for (let item of rmlist) {
+            this.modsService.delete(this.activeSession, item);
+        }
         this.sessionsService.storeData();
     }
 
-    /* 0 - not deleted, 1 - deleted value, 2 - deleted all values */
-    isDeleted(): number {
-        if ('deleted' in this.node) {
-            if (typeof this.node['deleted'] === 'boolean') {
-                if (this.node['deleted']) {
-                    return 2;
-                } else {
-                    return 0;
-                }
-            } else if (this.value) {
-                if (this.node['deleted'].indexOf(this.value) != -1) {
-                    return 1;
-                }
-            }
-        }
-        return 0;
+    deleteInstance(node) {
+        this.modsService.delete(this.activeSession, node);
+        this.sessionsService.storeData();
     }
 
     openCreatingDialog(element, node, parent = false) {
         if (parent) {
-            node = this.modsService.nodeParent(this.activeSession, node);
+            node = this.treeService.nodeParent(this.activeSession, node);
         }
         if (!('creatingChild' in node)) {
             this.sessionsService.childrenSchemas(this.activeSession.key, node['info']['path'], node).then(result => {
-                this.modsService.createOpen(result, node);
+                this.modsService.createOpen(this.activeSession, result, node);
             });
         } else if (element){
             /* scroll to the existing element */
@@ -157,7 +155,7 @@
     }
 
     closeCreatingDialog(node, reason='abort') {
-        this.modsService.createClose(node, reason);
+        this.modsService.createClose(this.activeSession, node, reason);
     }
 
     cancelModification(node, value = null) {
@@ -172,15 +170,15 @@
 }
 
 @Component({
-    selector: 'tree-view',
-    templateUrl: './tree.component.html',
+    selector: 'tree-node',
+    templateUrl: './tree-node.html',
     styleUrls: ['./tree.component.scss']
 })
 
-export class TreeView implements OnInit {
+export class TreeNode {
     @Input() node;
     @Input() indentation;
-    activeSession: Session;
+    @Input() activeSession: Session;
 
     constructor(private modsService: ModificationsService,
                 private sessionsService: SessionsService,
@@ -189,10 +187,6 @@
                 private changeDetector: ChangeDetectorRef,
                 private router: Router) {}
 
-    ngOnInit(): void {
-        this.activeSession = this.sessionsService.getActiveSession();
-    }
-
     inheritIndentation(node) {
         let newIndent;
         if (node['last']) {
@@ -269,7 +263,7 @@
 
     changeValue(node, target) {
         let input;
-        if (target.classList.contains('value')) {
+        if (target.classList.contains('value_inline')) {
             if (target.classList.contains('invalid')) {
                 return;
             }
@@ -279,7 +273,7 @@
         }
 
         if (node['info']['type'] == 8) {
-            this.modsService.change(this.activeSession, node, [input.value]);
+            this.modsService.change(this.activeSession, node, input.value);
         } else {
             this.modsService.change(this.activeSession, node, input.value);
         }
@@ -331,3 +325,23 @@
         }
     }
 }
+
+@Component({
+    selector: 'tree-view',
+    templateUrl: './tree.component.html',
+    styleUrls: ['./tree.component.scss']
+})
+
+export class TreeView implements OnInit {
+    @Input() node;
+    @Input() indentation;
+    activeSession: Session;
+
+    constructor(private sessionsService: SessionsService,
+                private treeService: TreeService) {}
+
+    ngOnInit(): void {
+        this.activeSession = this.sessionsService.getActiveSession();
+    }
+}
+
diff --git a/frontend/config/tree.service.ts b/frontend/config/tree.service.ts
new file mode 100644
index 0000000..11ffbee
--- /dev/null
+++ b/frontend/config/tree.service.ts
@@ -0,0 +1,230 @@
+import {Injectable} from '@angular/core';
+
+import {Session} from './session';
+
+@Injectable()
+export class TreeService {
+
+    constructor() {}
+
+    expandable(node): boolean {
+        if (node['info']['type'] == 1 || /* container */
+            node['info']['type'] == 16) { /* list */
+                return true;
+        }
+        return false;
+    }
+
+    nodeParent(activeSession: Session, node) {
+        if (node['path'] =='/') {
+            return null;
+        }
+
+        let match = false;
+        let parent = null;
+        let children = activeSession.data['children'];
+        let newChildren = activeSession.data['newChildren'];
+
+        while (children || newChildren) {
+            match = false;
+
+            if (children) {
+                for (let iter of children) {
+                    if (node['path'] == iter['path']) {
+                        match = true;
+                        children = null;
+                        newChildren = null;
+                        break;
+                    } else if (node['path'].startsWith(iter['path'] + '/')) {
+                        match = true;
+                        parent = iter;
+                        children = iter['children'];
+                        if (('new' in node) && ('newChildren' in iter)) {
+                            newChildren = iter['newChildren'];
+                        } else {
+                            newChildren = null;
+                        }
+                        break;
+                    }
+                }
+                if (!match) {
+                    children = null;
+                }
+            }
+            if (match) {
+                continue;
+            }
+            if (newChildren) {
+                for (let iter of newChildren) {
+                    if (node['path'] == iter['path']) {
+                        match = true;
+                        children = null;
+                        newChildren = null;
+                        break;
+                    } else if (node['path'].startsWith(iter['path'] + '/')) {
+                        match = true;
+                        parent = iter;
+                        children = iter['children'];
+                        if (('new' in node) && ('newChildren' in iter)) {
+                            newChildren = iter['newChildren'];
+                        } else {
+                            newChildren = null;
+                        }
+                        break;
+                    }
+                }
+                if (!match) {
+                    children = null;
+                }
+            }
+        }
+
+        if (!parent) {
+            parent = activeSession.data;
+        }
+        return parent;
+    }
+
+    inheritIndentation(indentation, node) {
+        let newIndent;
+        if (node['last'] || ('lastLeafList' in node)) {
+            newIndent = [true];
+        } else {
+            newIndent = [false];
+        }
+
+        if (!indentation) {
+            return newIndent;
+        } else {
+            return indentation.concat(newIndent);
+        }
+    }
+
+    childrenToShow(node) {
+        let result = [];
+        let nc_dup = [];
+        if ('newChildren' in node) {
+            nc_dup = node['newChildren'].slice();
+        }
+        if ('children' in node) {
+            let lastList = null;
+            for (let child of node['children']) {
+                if (lastList) {
+                    if (lastList['name'] == child['info']['name'] && lastList['module'] == child['info']['module']) {
+                        continue;
+                    } else {
+                        lastList = null;
+                    }
+                }
+                if (child['info']['type'] == 16 || child['info']['type'] == 8) {
+                    lastList = child['info'];
+                    for (let i = nc_dup.length - 1; i >= 0; i--) {
+                        if (lastList['name'] == nc_dup[i]['info']['name'] && lastList['module'] == nc_dup[i]['info']['module']) {
+                            nc_dup.splice(Number(i), 1);
+                        }
+                    }
+                }
+                result.push(child);
+            }
+        }
+        if (nc_dup.length) {
+            result = result.concat(nc_dup);
+        }
+        return result;
+    }
+
+    getInstances(activeSession, node, result = []) {
+        let parent = this.nodeParent(activeSession, node);
+        if ('children' in parent) {
+            for (let child of parent['children']) {
+                if (node['info']['name'] == child['info']['name'] && node['info']['module'] == child['info']['module']) {
+                    result.push(child);
+                }
+            }
+        }
+        if ('newChildren' in parent) {
+            for (let child of parent['newChildren']) {
+                if (node['info']['name'] == child['info']['name'] && node['info']['module'] == child['info']['module']) {
+                    result.push(child);
+                }
+            }
+        }
+        return result;
+    }
+
+    nodesToShow(activeSession, node) {
+        let result = [];
+        if (node['info']['type'] == 16) {
+            this.getInstances(activeSession, node, result);
+        } else if (node['info']['type'] == 8) {
+            if (node['first']) {
+                this.getInstances(activeSession, node, result);
+            }
+        } else {
+            result.push(node);
+        }
+        return result;
+    }
+
+    setDirty(activeSession, node) {
+        if (!activeSession.modifications) {
+            return;
+        }
+
+        if (node['path'] in activeSession.modifications) {
+            node['dirty'] = true;
+            if (activeSession.modifications[node['path']]['type'] == 'change') {
+                activeSession.modifications[node['path']]['original'] = node['value'];
+            }
+            node['value'] = activeSession.modifications[node['path']]['value']; 
+        }
+        /* recursion */
+        if ('children' in node) {
+            for (let child of node['children']) {
+                this.setDirty(activeSession, child);
+            }
+        }
+    }
+
+    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';
+            }
+        }
+    }
+}
diff --git a/frontend/netopeer.module.ts b/frontend/netopeer.module.ts
index 72f3097..c5daeb7 100644
--- a/frontend/netopeer.module.ts
+++ b/frontend/netopeer.module.ts
@@ -20,13 +20,14 @@
 import { InventorySchemasComponent } from './inventory/schemas.component';
 import { InventoryDevicesComponent } from './inventory/devices.component';
 import { ConfigComponent } from './config/config.component';
-import { TreeView, TreeIndent, TreeCreate, TreeScrollTo, CheckLeafValue } from './config/tree.component';
+import { TreeView, TreeNode, TreeIndent, TreeCreate, TreeScrollTo, CheckLeafValue } from './config/tree.component';
 import { YANGComponent } from './yang/yang.component';
 import { MonitoringComponent } from './monitoring/monitoring.component';
 import { PluginsComponent } from './plugins/plugins.component';
 
-import { SessionsService } from './config/sessions.service'
-import { SchemasService } from './yang/schemas.service'
+import { SessionsService } from './config/sessions.service';
+import { SchemasService } from './yang/schemas.service';
+import { TreeService } from './config/tree.service';
 
 const routes: Routes = [
   { path : 'netopeer', component : NetopeerComponent, canActivate : [AuthGuard],
@@ -103,6 +104,7 @@
     TreeScrollTo,
     TreeIndent,
     TreeCreate,
+    TreeNode,
     TreeView,
     YANGComponent,
     MonitoringComponent,
@@ -110,7 +112,8 @@
   ],
   providers: [
     SessionsService,
-    SchemasService
+    SchemasService,
+    TreeService
   ],
   entryComponents : [
     NetopeerComponent