Skip to content

Commit

Permalink
Improve the performance by OPC UA browser dialog #20
Browse files Browse the repository at this point in the history
With LazyForDirective
  • Loading branch information
unocelli committed May 14, 2019
1 parent e37a2d9 commit b0f9833
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 56 deletions.
265 changes: 217 additions & 48 deletions client/dist/main.bundle.js

Large diffs are not rendered by default.

181 changes: 181 additions & 0 deletions client/src/app/_directives/lazyFor.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import {
Input, Directive, ViewContainerRef,
OnInit, TemplateRef, DoCheck,
IterableDiffers, IterableDiffer
} from '@angular/core';

@Directive({
selector: '[lazyFor]'
})
export class LazyForDirective implements DoCheck, OnInit {

lazyForContainer: HTMLElement;

itemHeight: number;
itemTagName: string;

@Input()
set lazyForOf(list: any[]) {
this.list = list;

if (list) {
this.differ = this.iterableDiffers.find(list).create();

if (this.initialized) {
this.update();
}
}
}

private templateElem: HTMLElement;

private beforeListElem: HTMLElement;
private afterListElem: HTMLElement;

private list: any[] = [];

private initialized = false;
private firstUpdate = true;

private differ: IterableDiffer<any>;

private lastChangeTriggeredByScroll = false;

constructor(private vcr: ViewContainerRef,
private tpl: TemplateRef<any>,
private iterableDiffers: IterableDiffers) { }

ngOnInit() {
this.templateElem = this.vcr.element.nativeElement;

this.lazyForContainer = this.templateElem.parentElement;

//Adding an event listener will trigger ngDoCheck whenever the event fires so we don't actually need to call
//update here.
this.lazyForContainer.addEventListener('scroll', () => {
this.lastChangeTriggeredByScroll = true;
});

this.initialized = true;
}

ngDoCheck() {
if (this.differ && Array.isArray(this.list)) {

if (this.lastChangeTriggeredByScroll) {
this.update();
this.lastChangeTriggeredByScroll = false;
} else {
const changes = this.differ.diff(this.list);

if (changes !== null) {
this.update();
}
}
}
}

/**
* List update
*
* @returns {void}
*/
private update(): void {

//Can't run the first update unless there is an element in the list
if (this.list.length === 0) {
this.vcr.clear();
if (!this.firstUpdate) {
this.beforeListElem.style.height = '0';
this.afterListElem.style.height = '0';
}
return;
}

if (this.firstUpdate) {
this.onFirstUpdate();
}

const listHeight = this.lazyForContainer.clientHeight;
const scrollTop = this.lazyForContainer.scrollTop;

//The height of anything inside the container but above the lazyFor content
const fixedHeaderHeight =
(this.beforeListElem.getBoundingClientRect().top - this.beforeListElem.scrollTop) -
(this.lazyForContainer.getBoundingClientRect().top - this.lazyForContainer.scrollTop);

//This needs to run after the scrollTop is retrieved.
this.vcr.clear();

let listStartI = Math.floor((scrollTop - fixedHeaderHeight) / this.itemHeight);
listStartI = this.limitToRange(listStartI, 0, this.list.length);

let listEndI = Math.ceil((scrollTop - fixedHeaderHeight + listHeight) / this.itemHeight);
listEndI = this.limitToRange(listEndI, -1, this.list.length - 1);

for (let i = listStartI; i <= listEndI; i++) {
this.vcr.createEmbeddedView(this.tpl, {
$implicit: this.list[i],
index: i
});
}

this.beforeListElem.style.height = `${listStartI * this.itemHeight}px`;
this.afterListElem.style.height = `${(this.list.length - listEndI - 1) * this.itemHeight}px`;
}

/**
* First update.
*
* @returns {void}
*/
private onFirstUpdate(): void {

let sampleItemElem: HTMLElement;
if (this.itemHeight === undefined || this.itemTagName === undefined) {
this.vcr.createEmbeddedView(this.tpl, {
$implicit: this.list[0],
index: 0
});
sampleItemElem = <HTMLElement>this.templateElem.nextSibling;
}

if (this.itemHeight === undefined) {
this.itemHeight = sampleItemElem.clientHeight;
}

if (this.itemTagName === undefined) {
this.itemTagName = sampleItemElem.tagName;
}

this.beforeListElem = document.createElement(this.itemTagName);
this.templateElem.parentElement.insertBefore(this.beforeListElem, this.templateElem);

this.afterListElem = document.createElement(this.itemTagName);
this.templateElem.parentElement.insertBefore(this.afterListElem, this.templateElem.nextSibling);

// If you want to use <li> elements
if (this.itemTagName.toLowerCase() === 'li') {
this.beforeListElem.style.listStyleType = 'none';
this.afterListElem.style.listStyleType = 'none';
}

this.firstUpdate = false;
}

/**
* Limit To Range
*
* @param {number} num - Element number.
* @param {number} min - Min element number.
* @param {number} max - Max element number.
*
* @returns {number}
*/
private limitToRange(num: number, min: number, max: number) {
return Math.max(
Math.min(num, max),
min
);
}
}
4 changes: 3 additions & 1 deletion client/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { DialogDraggableDirective } from './_directives/dialog-draggable.directi
import { ModalPositionCache } from './_directives/modal-position.cache';
import { DraggableDirective } from './_directives/ngx-draggable.directive';
import { NumberOnlyDirective } from './_directives/number.directive';
import { LazyForDirective } from './_directives/lazyFor.directive';

import { GaugesManager } from './gauges/gauges.component';
import { GaugeBaseComponent } from './gauges/gauge-base/gauge-base.component';
Expand Down Expand Up @@ -104,7 +105,8 @@ import { GaugeSemaphoreComponent } from './gauges/controls/gauge-semaphore/gauge
NumberOnlyDirective,
NgxFabButtonComponent,
NgxFabItemButtonComponent,
TreetableComponent
TreetableComponent,
LazyForDirective
],
imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class TagPropertyComponent implements OnInit, OnDestroy {
this.treetable.setNodeProperty(values.node, this.attributeToString(values.node.attribute));
}
}
console.log(values);
// console.log(values);
});
}
this.queryNext(null);
Expand Down
3 changes: 2 additions & 1 deletion client/src/app/gui-helpers/treetable/treetable.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
}

.item {
width: 100%
width: 100%;
height: 40px;
}
.item:hover {
background-color: rgba(0,0,0,0.1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div #treetable class="container" [style.height]="containerProperty.height" [style.width]="containerProperty.width">
<div *ngFor="let node of nodeToItems(); index as i" class="item">
<div *lazyFor="let node of list; index as i" class="item">
<div *ngIf="node.visible">
<div class="item-text" [style.left.px]="node.childPos * 15" [style.width.px]="500 - node.childPos * 15">
<button mat-icon-button (click)="onExpandToggle(node)" enabled="node.childs" *ngIf="node.class === nodeType.Object">
Expand Down
9 changes: 5 additions & 4 deletions client/src/app/gui-helpers/treetable/treetable.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class TreetableComponent implements OnInit {
nodeType = NodeType;

nodes = {};
private list: any[] = [];

containerProperty = { width: '100%', height: '100%' };

Expand All @@ -36,15 +37,14 @@ export class TreetableComponent implements OnInit {
this.expand.emit(node);
this.hideNode(node, true);
} else {
node
this.hideNode(node, false);
}
}

hideNode(node: Node, visible: boolean) {
Object.values(node.childs).forEach((node) => {
node.visible = visible;
this.hideNode(node, (visible) ? node.expanded : visible);
Object.values(node.childs).forEach((n) => {
n.visible = visible;
this.hideNode(n, (visible) ? n.expanded : visible);
});
}

Expand All @@ -61,6 +61,7 @@ export class TreetableComponent implements OnInit {
if (Object.keys(this.nodes).indexOf(node.id) < 0) {
this.nodes[node.id] = node;
}
this.list = this.nodeToItems();
}

setNodeProperty(node: Node, pro: string) {
Expand Down

0 comments on commit b0f9833

Please sign in to comment.