frontend CHANGE refactor data modification
move data manipulation operations into a separate service to allow
better splitting to components without code/templates duplication
and communication between components via events
diff --git a/frontend/config/config.component.html b/frontend/config/config.component.html
index f9a7d95..003bf94 100644
--- a/frontend/config/config.component.html
+++ b/frontend/config/config.component.html
@@ -71,8 +71,8 @@
</div>
<ng-container *ngIf="activeSession.dataVisibility!='none' && activeSession.data">
<div id="config-data">
- <tree-view [node]="root" [root]="root"></tree-view>
- <!-- <pre *ngIf="root['schemaChildren']">{{root['schemaChildren'] | json}}</pre> -->
+ <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">
<div *ngIf="activeSession.modifications" class="msg-rounded" [style.width.px]="modificationsStatus.offsetWidth">
diff --git a/frontend/config/config.component.ts b/frontend/config/config.component.ts
index c916348..af12bff 100644
--- a/frontend/config/config.component.ts
+++ b/frontend/config/config.component.ts
@@ -1,6 +1,7 @@
import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
+import {ModificationsService} from './modifications.service';
import {SessionsService} from './sessions.service';
import {Session} from './session';
@@ -15,9 +16,10 @@
activeSession: Session;
err_msg = "";
loading = false;
- root: {};
- constructor(private sessionsService: SessionsService, private router: Router) {}
+ constructor(private sessionsService: SessionsService,
+ private modsService: ModificationsService,
+ private router: Router) {}
addSession() {
this.router.navigateByUrl('/netopeer/inventory/devices');
@@ -141,14 +143,20 @@
this.loading = true;
this.sessionsService.rpcGetSubtree(this.activeSession.key, all).subscribe(result => {
if (result['success']) {
- this.activeSession.data = result['data'];
- this.root['children'] = this.activeSession.data;
+ 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.root);
+ console.log(this.activeSession.data);
} else {
this.activeSession.dataVisibility = 'none';
if ('error-msg' in result) {
@@ -162,49 +170,9 @@
});
}
- cancelChangesNode(node, recursion = true) {
- if ('creatingChild' in node) {
- delete node['creatingChild'];
- }
- if ('deleted' in node) {
- node['dirty'] = false;
- node['deleted'] = false;
- }
-
- if (this.activeSession.modifications) {
- let record = this.sessionsService.getModificationsRecord(node['path']);
- if (record) {
- node['dirty'] = false;
- if (record['type'] == 'change') {
- node['value'] = record['original'];
- }
- this.sessionsService.removeModificationsRecord(node['path']);
- if (!this.activeSession.modifications) {
- return;
- }
- }
- }
-
- /* recursion */
- if (recursion && 'children' in node) {
- for (let child of node['children']) {
- this.cancelChangesNode(child);
- }
- if ('newChildren' in node) {
- for (let child of node['newChildren']) {
- this.sessionsService.removeModificationsRecord(child['path']);
- }
- delete node['newChildren'];
- if (('children' in node) && node['children'].length) {
- node['children'][node['children'].length - 1]['last'] = true;
- }
- }
- }
- }
-
cancelChanges() {
//console.log(JSON.stringify(this.activeSession.modifications))
- this.cancelChangesNode(this.root);
+ this.modsService.cancelModification(this.activeSession);
this.sessionsService.storeData();
//console.log(JSON.stringify(this.activeSession.modifications))
}
@@ -217,11 +185,6 @@
ngOnInit(): void {
this.sessionsService.checkSessions();
this.activeSession = this.sessionsService.getActiveSession();
- this.root = {};
- this.root['path'] = '/';
- this.root['info'] = {};
- this.root['info']['path'] = '/';
- this.root['children'] = this.activeSession.data;
}
changeActiveSession(key: string) {
diff --git a/frontend/config/modifications.service.ts b/frontend/config/modifications.service.ts
new file mode 100644
index 0000000..a0de997
--- /dev/null
+++ b/frontend/config/modifications.service.ts
@@ -0,0 +1,471 @@
+import { Injectable } from '@angular/core';
+
+import { Session} from './session';
+import { SessionsService } from './sessions.service';
+
+@Injectable()
+export class ModificationsService{
+
+ constructor(private sessionsService: SessionsService) {}
+
+ createModificationsRecord(activeSession, path) {
+ if (!activeSession.modifications) {
+ activeSession.modifications = {};
+ }
+
+ if (!(path in activeSession.modifications)) {
+ activeSession.modifications[path] = {};
+ }
+ return activeSession.modifications[path];
+ }
+
+ getModificationsRecord(activeSession, path) {
+ if (!activeSession.modifications) {
+ return null;
+ }
+
+ if (!(path in activeSession.modifications)) {
+ return null;
+ }
+ return activeSession.modifications[path];
+ }
+
+ removeModificationsRecord(activeSession, path = null) {
+ if (!activeSession.modifications) {
+ return;
+ }
+
+ if (path && (path in activeSession.modifications)) {
+ delete activeSession.modifications[path];
+ }
+
+ if (!Object.keys(activeSession.modifications).length) {
+ delete activeSession.modifications;
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ setEdit(node, set = true) {
+ if (set && node['info']['datatypebase'] == 'empty') {
+ node['value'] = '';
+ return;
+ }
+ node['edit'] = set;
+ }
+
+ setLast(list) {
+ let last;
+ for (let iter of list) {
+ delete iter['last'];
+ last = iter;
+ }
+ last['last'] = true;
+ }
+
+ nodeParent(activeSession, node) {
+ if (node['path'] =='/') {
+ return null;
+ }
+
+ let match = false;
+ let parent = null;
+ let children = activeSession.data['children'];
+ let 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'];
+ } else {
+ return child['name'];
+ }
+ }
+
+ delete(activeSession, node) {
+ if ('new' in node) {
+ /* removing newly created subtree */
+ let parent = this.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;
+ }
+ }
+ } 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'];
+ }
+ }
+ } else {
+ let record = this.createModificationsRecord(activeSession, node['path']);
+
+ if (!('type' in record)) {
+ /* new record */
+ record['type'] = 'delete';
+ record['original'] = node;
+ node['deleted'] = true;
+ node['dirty'] = true;
+ } else if (record['type'] == 'change') {
+ record['type'] = 'delete';
+ node['value'] = record['original'];
+ delete record['original'];
+ delete record['value'];
+ node['deleted'] = true;
+ }
+ }
+ }
+
+ change(activeSession, node, leafValue) {
+ if (!('new' in node)) {
+ let 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 */
+ this.setEdit(node, false);
+ this.removeModificationsRecord(activeSession);
+ return;
+ }
+ record['type'] = 'change';
+ record['original'] = node['value'];
+ 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;
+ }
+ }
+
+ node['value'] = leafValue;
+ this.setEdit(node, false);
+ }
+
+ createOpen(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'];
+ }
+ }
+ }
+
+ createClose(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;
+ }
+ }
+ delete node['creatingChild'];
+ delete node['schemaChildren'];
+ }
+
+ create(activeSession, node, index) {
+ //console.trace();
+ let newNode = {};
+ newNode['new'] = true;
+ newNode['info'] = node['schemaChildren'][index];
+ if (node['path'] == '/') {
+ newNode['path'] = '/' + this.schemaName(node['info'], newNode['info']);
+ } else {
+ newNode['path'] = node['path'] + '/' + this.schemaName(node['info'], newNode['info']);
+ }
+ newNode['dirty'] = true;
+
+ if ('new' in node) {
+ if (!('children' in node)) {
+ node['children'] = []
+ }
+ node['children'].push(newNode)
+ } else {
+ if (!('newChildren' in node)) {
+ node['newChildren'] = [];
+ }
+ node['newChildren'].push(newNode);
+ }
+
+ switch(newNode['info']['type']) {
+ case 1: { /* container */
+ node['schemaChildren'].splice(index, 1);
+
+ 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);
+ });
+ break;
+ }
+ case 4: { /* leaf */
+ node['schemaChildren'].splice(index, 1);
+
+ newNode['value'] = newNode['info']['default'];
+ this.setEdit(newNode, true)
+ break;
+ }
+ case 16: { /* list */
+ let search;
+ if ('new' in node) {
+ search = node['children'];
+ } else {
+ search = node['newChildren'];
+ }
+ let pos = 1;
+ if (search.length) {
+ for (let sibling of search) {
+ if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') {
+ let n = parseInt(sibling['path'].substring(newNode['path'].length + 1));
+ if (n >= pos) {
+ pos = n + 1;
+ }
+ }
+ }
+ }
+ newNode['path'] = newNode['path'] + '[' + pos + ']';
+
+ newNode['children'] = [];
+ /* open creation dialog for nodes inside the created list */
+ this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
+ this.createOpen(result, newNode);
+
+ if (newNode['schemaChildren'].length) {
+ for (let i in newNode['schemaChildren']) {
+ if (!newNode['schemaChildren'][i]['key']) {
+ continue;
+ }
+ let newKey = {};
+ newKey['new'] = true;
+ newKey['info'] = newNode['schemaChildren'][i];
+ newKey['path'] = newNode['path'] + '/' + this.schemaName(newNode['info'], newKey['info']);
+ newKey['dirty'] = true;
+ this.setEdit(newKey, true)
+ newNode['children'].push(newKey)
+ newNode['schemaChildren'].splice(i, 1);
+ }
+ }
+ });
+
+ break;
+ }
+ }
+
+ if (!node['schemaChildren'].length) {
+ newNode['last'] = true;
+ this.createClose(node, 'success');
+ }
+
+ if (!('new' in node)) {
+ /* store the record about the newly created data */
+ let record = this.createModificationsRecord(activeSession, newNode['path']);
+ if (('type' in record) && record['type'] == 'delete') {
+ record['type'] = 'change';
+ record['value'] = newNode;
+ delete record['original']['deleted'];
+ for (let i in node['children']) {
+ if (node['children'][i] == record['original']) {
+ node['children'].splice(i, 1);
+ break;
+ }
+ }
+ } else {
+ record['type'] = 'create';
+ record['data'] = newNode;
+ }
+ }
+ console.log(node)
+ }
+
+ cancelModification(activeSession, node = activeSession.data, recursion = true) {
+ if ('creatingChild' in node) {
+ delete node['creatingChild'];
+ }
+ if ('deleted' in node) {
+ node['dirty'] = false;
+ node['deleted'] = false;
+ }
+
+ if ('new' in node) {
+ /* removing newly created subtree */
+ let parent = this.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) {
+ if (Number(i) > 0 && parent['children'][i]['last']) {
+ parent['children'][Number(i) - 1]['last'] = true;
+ }
+ parent['children'].splice(i, 1);
+ break;
+ }
+ }
+ } else {
+ this.removeModificationsRecord(activeSession, node['path']);
+ for (let i in parent['newChildren']) {
+ if (parent['newChildren'][i]['path'] == node['path']) {
+ if (Number(i) > 0 && parent['newChildren'][i]['last']) {
+ parent['newChildren'][Number(i) - 1]['last'] = true;
+ }
+ parent['newChildren'].splice(i, 1);
+ break;
+ }
+ }
+ if (!parent['newChildren'].length) {
+ delete parent['newChildren'];
+ }
+ }
+
+ if (node['info']['type'] == 1 || node['info']['type'] == 4) {
+ /* fix the list of nodes to create in parent */
+ let schemas;
+ if (!('schemaChildren' in parent)) {
+ schemas = [];
+ } else {
+ schemas = parent['schemaChildren'];
+ }
+ schemas.push(node['info']);
+ this.createOpen(schemas, parent)
+ }
+ } else if (activeSession.modifications) {
+ let record = this.getModificationsRecord(activeSession, node['path']);
+ if (record) {
+ node['dirty'] = false;
+ if (record['type'] == 'change') {
+ node['value'] = record['original'];
+ }
+ this.removeModificationsRecord(activeSession, node['path']);
+ if (!activeSession.modifications) {
+ return;
+ }
+ }
+ }
+
+ /* recursion */
+ if (recursion && 'children' in node) {
+ if ('newChildren' in node) {
+ for (let child of node['newChildren']) {
+ let record = this.getModificationsRecord(activeSession, child['path']);
+ if (record['type'] == 'change') {
+ if (node['children'].length) {
+ node['children'][node['children'].length - 1]['last'] = false;
+ }
+ record['original']['last'] = true;
+ node['children'].push(record['original'])
+ }
+ this.removeModificationsRecord(activeSession, child['path']);
+ }
+ delete node['newChildren'];
+ if (('children' in node) && node['children'].length) {
+ node['children'][node['children'].length - 1]['last'] = true;
+ }
+ }
+ let last;
+ for (let child of node['children']) {
+ this.cancelModification(activeSession, child);
+ delete child['last'];
+ last = child;
+ }
+ last['last'] = true;
+ }
+ }
+}
diff --git a/frontend/config/sessions.service.ts b/frontend/config/sessions.service.ts
index 68fa136..3d3549b 100644
--- a/frontend/config/sessions.service.ts
+++ b/frontend/config/sessions.service.ts
@@ -81,45 +81,6 @@
}
}
- createModificationsRecord(path) {
- let activeSession = this.getActiveSession();
- if (!activeSession.modifications) {
- activeSession.modifications = {};
- }
-
- if (!(path in activeSession.modifications)) {
- activeSession.modifications[path] = {};
- }
- return activeSession.modifications[path];
- }
-
- getModificationsRecord(path) {
- let activeSession = this.getActiveSession();
- if (!activeSession.modifications) {
- return null;
- }
-
- if (!(path in activeSession.modifications)) {
- return null;
- }
- return activeSession.modifications[path];
- }
-
- removeModificationsRecord(path = null) {
- let activeSession = this.getActiveSession();
- if (!activeSession.modifications) {
- return;
- }
-
- if (path && (path in activeSession.modifications)) {
- delete activeSession.modifications[path];
- }
-
- if (!Object.keys(activeSession.modifications).length) {
- delete activeSession.modifications;
- }
- }
-
checkValue(key: string, path: string, value: string): Observable<string[]> {
let params = new URLSearchParams();
params.set('key', key);
@@ -216,19 +177,7 @@
return this.http.get('/netopeer/session/rpcGet', options)
.map((resp: Response) => {
//console.log(resp);
- let result = resp.json();
- if (result['success']) {
- if (path.length) {
- for (let iter of result['data']['children']) {
- this.setDirty(iter);
- }
- } else {
- for (let iter of result['data']) {
- this.setDirty(iter);
- }
- }
- }
- return result;
+ return resp.json();
})
.catch((err: Response | any) => Observable.throw(err));
}
diff --git a/frontend/config/tree-create.html b/frontend/config/tree-create.html
new file mode 100644
index 0000000..26a6ef3
--- /dev/null
+++ b/frontend/config/tree-create.html
@@ -0,0 +1,20 @@
+<!-- create new child dialog -->
+<div *ngIf="node['creatingChild']" id="{{node['path']}}_createChildDialog"
+ class="node_edit" [class.dialog]="node['schemaChildren']"
+ treeScrollTo [node]="node">
+ <ng-container *ngIf="node['schemaChildren'].length; else nothingToCreate">
+ <tree-indent [node]="node" [indentation]="indentation" [type]="'edit'"></tree-indent>
+ <select (change)="creatingDialogSelect(node, $event.target.value, $event.currentTarget)">
+ <option disabled selected value="-1" style="display: none"> </option>
+ <ng-container *ngFor="let schema of node['schemaChildren']; let i='index'">
+ <option value="{{i}}">{{modsService.schemaName(node['info'], schema)}}</option>
+ </ng-container>
+ </select>
+ <!-- <pre>{{node['schemaChildren'] | json}}</pre> -->
+ </ng-container>
+ <ng-template #nothingToCreate>
+ <div class="msg-failure msg-rounded">
+ <span class="msg-close" (click)="closeCreatingDialog(node);">x</span>There is no element to create at {{node['path']}}.
+ </div>
+ </ng-template>
+</div>
\ No newline at end of file
diff --git a/frontend/config/tree-indent.html b/frontend/config/tree-indent.html
index 9a29285..2d3fd82 100644
--- a/frontend/config/tree-indent.html
+++ b/frontend/config/tree-indent.html
@@ -3,15 +3,11 @@
onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
onmouseout="this.src='assets/netopeer/icons/close.svg'"
(click)="closeCreatingDialog(node)"/>
- <img *ngIf="node['info']['type'] == 4 && (type == 'edit' || node['key'])" class="icon_action" src="assets/netopeer/icons/tree_empty.svg"/>
- <img *ngIf="type != 'edit' && node['dirty'] && !node['deleted'] && !node['key']" class="icon_action" src="assets/netopeer/icons/close.svg"
+ <img *ngIf="node['info']['type'] == 4 && (type == 'edit' || node['info']['key'])" class="icon_action" src="assets/netopeer/icons/tree_empty.svg"/>
+ <img *ngIf="type != 'edit' && node['dirty'] && !node['info']['key']" 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)"/>
- <img *ngIf="node['deleted']" 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)="cancelDelete(node)"/>
+ (click)="cancelModification(node)"/>
</ng-container>
<ng-template #menu >
<span *ngIf="type == 'root' || node['info']['config']; else empty"
@@ -21,7 +17,7 @@
onmouseover="this.src='assets/netopeer/icons/menu_active.svg'"
onmouseout="this.src='assets/netopeer/icons/menu.svg'"/>
<div class="editmenu msg-rounded">
- <div *ngIf="type != 'root'" class="button_action" title="remove subtree {{node['path']}}"
+ <div *ngIf="type != 'root' && !node['info']['key']" class="button_action" title="remove subtree {{node['path']}}"
onmouseover="this.firstElementChild.src='assets/netopeer/icons/close_active.svg'"
onmouseout="this.firstElementChild.src='assets/netopeer/icons/close.svg'"
(click)="deleteSubtree(node);hideEditMenu($event.currentTarget.parentElement);">
diff --git a/frontend/config/tree.component.html b/frontend/config/tree.component.html
index 89c023b..1c42f86 100644
--- a/frontend/config/tree.component.html
+++ b/frontend/config/tree.component.html
@@ -1,47 +1,25 @@
<div *ngIf="node['path'] == '/'">
<!-- recursion - show children -->
<div class="node">
- <tree-indent [node]="node" [indentation]="indentation" [type]="'root'"
- (onOpenCreatingDialog)="openCreatingDialog($event.element, $event.node, $event.parent)">
- </tree-indent>
+ <tree-indent [node]="node" [indentation]="indentation" [type]="'root'"></tree-indent>
</div>
<div class="children" *ngIf="node['children'] || node['newChildren']">
<ng-container *ngFor="let child of node['children']">
- <tree-view [node]="child" [root]="root"></tree-view>
+ <tree-view [node]="child"></tree-view>
</ng-container>
<ng-container *ngFor="let child of newChildrenToShow(node)">
- <tree-view [node]="child" [root]="root"></tree-view>
+ <tree-view [node]="child"></tree-view>
</ng-container>
</div>
<!-- create new child dialog -->
- <div id="{{node['path']}}_createChildDialog" class="node_edit" *ngIf="node['creatingChild']" [class.dialog]="node['schemaChildren']"
- treeScrollTo [node]="node">
- <ng-container *ngIf="node['schemaChildren'].length; else nothingToCreate">
- <tree-indent [node]="node" [indentation]="indentation" [type]="'edit'"
- (onCloseCreatingDialog)="closeCreatingDialog($event)"></tree-indent>
- <select #nodeSelect (change)="creatingDialogSelect(node, $event.target.value, $event.currentTarget)">
- <option disabled selected value="-1" style="display:none"> </option>
- <ng-container *ngFor="let schema of node['schemaChildren']; let i='index'">
- <option value="{{i}}">{{schemaInfoName(node, schema)}}</option>
- </ng-container>
- </select>
- <!-- <pre>{{node['schemaChildren'] | json}}</pre> -->
- </ng-container>
- <ng-template #nothingToCreate>
- <div class="msg-failure msg-rounded">
- <span class="msg-close" (click)="closeCreatingDialog(node);">x</span>There is no element to create at {{node['path']}}.
- </div>
- </ng-template>
- </div>
+ <tree-create [node]="node" [indentation]="indentation"></tree-create>
</div>
<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]="node['deleted']">
- <tree-indent [node]="node" [indentation]="indentation"
- (onDeleteSubtree)="deleteSubtree($event)" (onOpenCreatingDialog)="openCreatingDialog($event.element, $event.node, $event.parent)">
- </tree-indent>
+ <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'"/>
@@ -52,7 +30,7 @@
<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['info']['key'] && node['info']['datatypebase'] != 'empty'">
+ <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);"
@@ -102,9 +80,7 @@
<!-- leaf's edit value -->
<ng-container *ngIf="node['info']['type'] == 4">
<div class="node_edit" [class.dirty]="node['dirty']" *ngIf="node['edit']">
- <tree-indent [node]="node" [indentation]="inheritIndentation(node)" [type]="'edit'"
- (onCloseCreatingDialog)="closeCreatingDialog($event)"
- (onDeleteSubtree)="deleteSubtree($event)"></tree-indent>
+ <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)="node['edit']=false" (keyup.enter)="node['edit']=false"
@@ -137,32 +113,13 @@
</div>
<div class="children" *ngIf="(node['children'] || node['newChildren']) && !node['deleted']">
<ng-container *ngFor="let child of node['children']">
- <tree-view [node]="child" [root]="root" [indentation]="inheritIndentation(node)"></tree-view>
+ <tree-view [node]="child" [indentation]="inheritIndentation(node)"></tree-view>
</ng-container>
<ng-container *ngFor="let child of newChildrenToShow(node)">
- <tree-view [node]="child" [root]="root" [indentation]="inheritIndentation(node)"></tree-view>
+ <tree-view [node]="child" [indentation]="inheritIndentation(node)"></tree-view>
</ng-container>
</div>
<!-- create new child dialog -->
- <div id="{{node['path']}}_createChildDialog" class="node_edit" *ngIf="node['creatingChild']" [class.dialog]="node['schemaChildren']"
- treeScrollTo [node]="node">
- <ng-container *ngIf="node['schemaChildren'].length; else nothingToCreate">
- <tree-indent [node]="node" [indentation]="inheritIndentation(node)" [type]="'edit'"
- (onCloseCreatingDialog)="closeCreatingDialog($event)"></tree-indent>
- <select #nodeSelect (change)="creatingDialogSelect(node, $event.target.value, $event.currentTarget)">
- <option disabled selected value="-1" style="display:none"> </option>
- <ng-container *ngFor="let schema of node['schemaChildren']; let i='index'">
- <option value="{{i}}">{{schemaInfoName(node, schema)}}</option>
- </ng-container>
- </select>
- <!-- <pre>{{node['schemaChildren'] | json}}</pre> -->
- </ng-container>
- <ng-template #nothingToCreate>
- <div class="msg-failure msg-rounded">
- <span class="msg-close" (click)="closeCreatingDialog(node);">x</span>There is no element to create at {{node['path']}}.
- </div>
- </ng-template>
- </div>
-
+ <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 6df3b8e..29571be 100644
--- a/frontend/config/tree.component.scss
+++ b/frontend/config/tree.component.scss
@@ -52,10 +52,7 @@
}
}
.node_edit.dialog {
- background-color: $colorChanged;
- border: 2px solid $colorChangedBorder;
- border-left-width: 0px;
- border-right-width: 0px;
+ background-color: $colorChanged;;
}
.status,
diff --git a/frontend/config/tree.component.ts b/frontend/config/tree.component.ts
index 3cde0a1..5c79843 100644
--- a/frontend/config/tree.component.ts
+++ b/frontend/config/tree.component.ts
@@ -2,6 +2,7 @@
import {Router} from '@angular/router';
import {Session} from './session';
+import {ModificationsService} from './modifications.service';
import {SessionsService} from './sessions.service';
@Directive({
@@ -27,6 +28,7 @@
@Output() onCheckValue = new EventEmitter();
constructor(private elRef:ElementRef) {}
+
ngAfterContentInit() {
console.log(this.node)
let node = this.node;
@@ -37,6 +39,35 @@
}
@Component({
+ selector: 'tree-create',
+ templateUrl: 'tree-create.html',
+ styleUrls: ['./tree.component.scss']
+})
+export class TreeCreate implements OnInit {
+ @Input() node;
+ @Input() indentation;
+ activeSession: Session;
+
+ constructor(private modsService: ModificationsService, private sessionsService: SessionsService) {}
+
+ ngOnInit(): void {
+ this.activeSession = this.sessionsService.getActiveSession();
+ }
+
+ closeCreatingDialog(node, reason='abort') {
+ this.modsService.createClose(node, reason);
+ }
+
+ creatingDialogSelect(node, index, source) {
+ this.modsService.create(this.activeSession, node, index);
+ this.sessionsService.storeData();
+ if (node['schemaChildren'].length) {
+ source.selectedIndex = 0;
+ }
+ }
+}
+
+@Component({
selector: 'tree-indent',
templateUrl: 'tree-indent.html',
styleUrls: ['./tree.component.scss']
@@ -45,14 +76,10 @@
@Input() node;
@Input() indentation;
@Input() type = "current";
- @Output() onShowEditMenu = new EventEmitter();
- @Output() onDeleteSubtree = new EventEmitter();
- @Output() onOpenCreatingDialog = new EventEmitter();
- @Output() onCloseCreatingDialog = new EventEmitter();
activeSession: Session;
timeout;
- constructor(private sessionsService: SessionsService) {}
+ constructor(private modsService: ModificationsService, private sessionsService: SessionsService) {}
ngOnInit(): void {
this.activeSession = this.sessionsService.getActiveSession();
@@ -73,19 +100,31 @@
}
deleteSubtree(node) {
- this.onDeleteSubtree.emit(node);
- }
- openCreatingDialog(element, node, parent) {
- this.onOpenCreatingDialog.emit({element, node, parent});
- }
- closeCreatingDialog(node) {
- this.onCloseCreatingDialog.emit(node);
+ this.modsService.delete(this.activeSession, node);
+ this.sessionsService.storeData();
}
- cancelDelete(node) {
- node['dirty'] = false;
- node['deleted'] = false;
- this.sessionsService.removeModificationsRecord(node['path']);
+ openCreatingDialog(element, node, parent = false) {
+ if (parent) {
+ node = this.modsService.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);
+ });
+ } else if (element){
+ /* scroll to the existing element */
+ element.ownerDocument.getElementById(node['path'] + '_createChildDialog').scrollIntoView(false);
+ }
+ }
+
+ closeCreatingDialog(node, reason='abort') {
+ this.modsService.createClose(node, reason);
+ }
+
+ cancelModification(node) {
+ this.modsService.cancelModification(this.activeSession, node, false);
+ this.sessionsService.storeData();
}
}
@@ -97,10 +136,11 @@
export class TreeView implements OnInit {
@Input() node;
- @Input() root;
@Input() indentation;
activeSession: Session;
- constructor(private sessionsService: SessionsService, private changeDetector: ChangeDetectorRef) {}
+ constructor(private modsService: ModificationsService,
+ private sessionsService: SessionsService,
+ private changeDetector: ChangeDetectorRef) {}
ngOnInit(): void {
this.activeSession = this.sessionsService.getActiveSession();
@@ -122,12 +162,12 @@
}
startEditing(node, target) {
- if (node['info']['key'] && !node['new']) {
+ if ((node['info']['key'] && !node['new']) || node['deleted']) {
return;
}
let parent = target.parentElement;
- this.setNodeEdit(node, true)
+ this.modsService.setEdit(node, true)
this.changeDetector.detectChanges();
parent.nextElementSibling.lastElementChild.focus();
@@ -161,7 +201,7 @@
changeValueCancel(node) {
if ('value' in node) {
- node['edit'] = false;
+ this.modsService.setEdit(node, false);
}
}
@@ -176,318 +216,10 @@
input = target.nextElementSibling;
}
- if (!('new' in node)) {
- let record = this.sessionsService.createModificationsRecord(node['path']);
- if (!('type' in record)) {
- console.log(record);
- /* new record */
- if (node['value'] == input['value']) {
- /* no change to the original value */
- this.setNodeEdit(node, false);
- this.sessionsService.removeModificationsRecord();
- return;
- }
- record['type'] = 'change';
- record['original'] = node['value'];
- record['value'] = input.value;
- node['dirty'] = true;
- } else if (record['type'] == 'change' && record['original'] == input['value']) {
- console.log(record);
- /* change to the original value, remove the change record */
- this.sessionsService.removeModificationsRecord(node['path']);
- node['dirty'] = false;
- } else {
- console.log(record);
- /* another change of existing change record */
- record['value'] = input.value;
- node['dirty'] = true;
- }
- console.log(this.activeSession.modifications);
- }
-
- node['value'] = input.value;
- this.setNodeEdit(node, false);
- console.log(this.activeSession.data);
+ this.modsService.change(this.activeSession, node, input.value);
this.sessionsService.storeData();
}
- deleteSubtree(node) {
- if ('new' in node) {
- /* removing newly created subtree */
- let parent = this.nodeParent(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;
- }
- }
- } else {
- this.sessionsService.removeModificationsRecord(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'];
- }
- }
- } else {
- let record = this.sessionsService.createModificationsRecord(node['path']);
-
- if (!('type' in record)) {
- /* new record */
- record['type'] = 'delete';
- node['deleted'] = true;
- node['dirty'] = true;
- } else if (record['type'] == 'change') {
- record['type'] = 'delete';
- node['value'] = record['original'];
- delete record['original'];
- delete record['value'];
- node['deleted'] = true;
- }
- }
- console.log(this.activeSession.modifications);
- this.sessionsService.storeData();
- }
-
- nodeParent(node) {
- let match = false;
- let parent = null;
- let children = this.activeSession.data;
- let 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 = this.root;
- }
-
- return parent;
- }
-
- schemaInfoName(parentNode, childSchema):string {
- if (parentNode['info']['module'] != childSchema['module']) {
- return childSchema['module'] + ':' + childSchema['name'];
- } else {
- return childSchema['name'];
- }
- }
-
- setNodeEdit(node, value) {
- if (value && node['info']['datatypebase'] == 'empty') {
- node['value'] = '';
- return;
- }
- node['edit'] = value;
- }
-
- creatingDialogSelect(node, index, source) {
- let newNode = {};
- newNode['new'] = true;
- newNode['info'] = node['schemaChildren'][index];
- if (node['path'] == '/') {
- newNode['path'] = '/' + this.schemaInfoName(node, newNode['info']);
- } else {
- newNode['path'] = node['path'] + '/' + this.schemaInfoName(node, newNode['info']);
- }
- newNode['dirty'] = true;
-
- if ('new' in node) {
- if (!('children' in node)) {
- node['children'] = []
- }
- node['children'].push(newNode)
- } else {
- if (!('newChildren' in node)) {
- node['newChildren'] = [];
- }
- node['newChildren'].push(newNode);
- }
-
- switch(newNode['info']['type']) {
- case 1: { /* container */
- newNode['children'] = [];
- node['schemaChildren'].splice(index, 1);
- this.openCreatingDialog(null, newNode);
- break;
- }
- case 4: { /* leaf */
- newNode['value'] = newNode['info']['default'];
- this.setNodeEdit(newNode, true)
- node['schemaChildren'].splice(index, 1);
- break;
- }
- case 16: { /* list */
- let search;
- if ('new' in node) {
- search = node['children'];
- } else {
- search = node['newChildren'];
- }
- let pos = 1;
- if (search.length) {
- for (let sibling of search) {
- console.log(sibling);
- console.log('testing ' + sibling['path'].substr(0, newNode['path'].length + 1));
- if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') {
- console.log(sibling['path'].substring(newNode['path'].length + 1))
- let n = parseInt(sibling['path'].substring(newNode['path'].length + 1));
- console.log('found ' + n + ' in ' + sibling['path'])
- if (n >= pos) {
- pos = n + 1;
- }
- }
- }
- }
- newNode['path'] = newNode['path'] + '[' + pos + ']';
- console.log(newNode['path'])
-
- newNode['children'] = [];
- this.openCreatingDialog(null, newNode);
-
- /* wait to receive schemaChildren of the list */
- (function wait(context, flag: boolean) {
- console.log(context)
- setTimeout(() => {
- if ('schemaChildren' in newNode) {
- if (newNode['schemaChildren'].length) {
- console.log(newNode);
- for (let i in newNode['schemaChildren']) {
- if (!newNode['schemaChildren'][i]['key']) {
- continue;
- }
- let newKey = {};
- newKey['new'] = true;
- newKey['key'] = true;
- newKey['info'] = newNode['schemaChildren'][i];
- newKey['path'] = newNode['path'] + '/' + context.schemaInfoName(newNode, newKey['info']);
- newKey['dirty'] = true;
- context.setNodeEdit(newKey, true)
- newNode['children'].push(newKey)
- newNode['schemaChildren'].splice(i, 1);
- }
- }
- } else {
- this.wait(context, true);
- }
- }, 10);
- })(this, true);
- break;
- }
- }
- if (!node['schemaChildren'].length) {
- newNode['last'] = true;
- this.closeCreatingDialog(node, 'success');
- } else {
- source.selectedIndex = 0;
- }
-
- if (!('new' in node)) {
- let record = this.sessionsService.createModificationsRecord(newNode['path']);
- record['type'] = 'create';
- record['data'] = newNode;
- }
- console.log(newNode)
- }
-
- openCreatingDialog(element, node, parent = false) {
- if (parent) {
- node = this.nodeParent(node);
- }
- if (!('creatingChild' in node)) {
- this.sessionsService.childrenSchemas(this.activeSession.key, node['info']['path'], node).then(result => {
- console.log(result)
- node['schemaChildren'] = result;
- node['creatingChild'] = {};
- });
- } else if (element){
- /* scroll to the existing element */
- element.ownerDocument.getElementById(node['path'] + '_createChildDialog').scrollIntoView(false);
- }
- if (('children' in node) && node['children'].length) {
- node['children'][node['children'].length - 1]['last'] = false;
- }
- console.log(node);
- }
-
- closeCreatingDialog(node, reason='abort') {
- console.log(node)
- if (reason == 'abort' && ('children' in node) && node['children'].length) {
- node['children'][node['children'].length - 1]['last'] = true;
- }
- delete node['creatingChild'];
- delete node['schemaChildren'];
- if ('new' in node && !node['children'].length) {
- let parent = this.nodeParent(node);
- for (let i in parent['children']) {
- if (parent['children'][i] == node) {
- if (!('schemaChildren' in parent)) {
- parent['schemaChildren'] = [];
- parent['creatingChild'] = {};
- }
- parent['schemaChildren'].push(node['info']);
- parent['children'].splice(i, 1);
- break;
- }
- }
- }
- }
-
expandable(node): boolean {
if (node['info']['type'] == 1 || /* container */
node['info']['type'] == 16) { /* list */
@@ -532,6 +264,9 @@
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);
diff --git a/frontend/netopeer.module.ts b/frontend/netopeer.module.ts
index e030f7b..9c33886 100644
--- a/frontend/netopeer.module.ts
+++ b/frontend/netopeer.module.ts
@@ -20,12 +20,13 @@
import { InventorySchemasComponent } from './inventory/schemas.component';
import { InventoryDevicesComponent } from './inventory/devices.component';
import { ConfigComponent } from './config/config.component';
-import { TreeView, TreeIndent, TreeScrollTo, CheckLeafValue } from './config/tree.component';
+import { TreeView, 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 { ModificationsService } from './config/modifications.service'
const routes: Routes = [
{ path : 'netopeer', component : NetopeerComponent, canActivate : [AuthGuard],
@@ -97,13 +98,15 @@
CheckLeafValue,
TreeScrollTo,
TreeIndent,
+ TreeCreate,
TreeView,
YANGComponent,
MonitoringComponent,
PluginsComponent
],
providers: [
- SessionsService
+ SessionsService,
+ ModificationsService
],
entryComponents : [
NetopeerComponent