refactor(benchmarks): make tree benchmark work again
This commit is contained in:

committed by
Victor Berchet

parent
5ff14de1f3
commit
61002733bc
72
modules/benchmarks/src/tree/app/util.ts
Normal file
72
modules/benchmarks/src/tree/app/util.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import {
|
||||
getIntParameter,
|
||||
windowProfile,
|
||||
windowProfileEnd
|
||||
} from '@angular/platform-browser/testing/benchmark_util';
|
||||
|
||||
export class TreeNode {
|
||||
constructor(public value: string, public left: TreeNode, public right: TreeNode) {
|
||||
}
|
||||
}
|
||||
|
||||
let treeCreateCount: number;
|
||||
let maxDepth: number;
|
||||
let numberData: string[];
|
||||
let charData: string[];
|
||||
|
||||
init();
|
||||
|
||||
function init() {
|
||||
maxDepth = getIntParameter('depth');
|
||||
treeCreateCount = 0;
|
||||
numberData = [];
|
||||
charData = [];
|
||||
for (let i = 0; i<maxDepth; i++) {
|
||||
numberData.push(i.toString());
|
||||
charData.push(String.fromCharCode('A'.charCodeAt(0) + i));
|
||||
}
|
||||
}
|
||||
|
||||
function _buildTree(values: string[], curDepth: number = 0): TreeNode {
|
||||
if (maxDepth === curDepth) return new TreeNode('', null, null);
|
||||
return new TreeNode(values[curDepth], _buildTree(values, curDepth + 1),
|
||||
_buildTree(values, curDepth + 1));
|
||||
}
|
||||
|
||||
export function emptyTree(): TreeNode {
|
||||
return new TreeNode('', null, null);
|
||||
}
|
||||
|
||||
export function buildTree(): TreeNode {
|
||||
treeCreateCount++;
|
||||
return _buildTree(treeCreateCount % 2 ? numberData : charData);
|
||||
}
|
||||
|
||||
export function profile(create: () => void, destroy: () => void, name: string) {
|
||||
return function() {
|
||||
windowProfile(name + ' w GC');
|
||||
var duration = 0;
|
||||
var count = 0;
|
||||
while (count++ < 150) {
|
||||
(<any>window)['gc']();
|
||||
var start = window.performance.now();
|
||||
create();
|
||||
duration += window.performance.now() - start;
|
||||
destroy();
|
||||
}
|
||||
windowProfileEnd(name + ' w GC');
|
||||
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
|
||||
|
||||
windowProfile(name + ' w/o GC');
|
||||
duration = 0;
|
||||
count = 0;
|
||||
while (count++ < 150) {
|
||||
var start = window.performance.now();
|
||||
create();
|
||||
duration += window.performance.now() - start;
|
||||
destroy();
|
||||
}
|
||||
windowProfileEnd(name + ' w/o GC');
|
||||
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
|
||||
};
|
||||
}
|
71
modules/benchmarks/src/tree/baseline/app/tree.ts
Normal file
71
modules/benchmarks/src/tree/baseline/app/tree.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {TreeNode} from '../../app/util';
|
||||
|
||||
// http://jsperf.com/nextsibling-vs-childnodes
|
||||
|
||||
const BASELINE_TREE_TEMPLATE =document.createElement('template');
|
||||
BASELINE_TREE_TEMPLATE.innerHTML = '<span>_<template class="ng-provider"></template><template class="ng-provider"></template></span>';
|
||||
const BASELINE_IF_TEMPLATE =document.createElement('template');
|
||||
BASELINE_IF_TEMPLATE.innerHTML = '<span template="if"><tree></tree></span>';
|
||||
|
||||
export class BaseLineTreeComponent {
|
||||
value: BaseLineInterpolation;
|
||||
left: BaseLineIf;
|
||||
right: BaseLineIf;
|
||||
constructor(public element: HTMLElement) {
|
||||
var clone = getDOM().clone(BASELINE_TREE_TEMPLATE.content.firstChild);
|
||||
getDOM().appendChild(element, clone);
|
||||
|
||||
var child = clone.firstChild;
|
||||
this.value = new BaseLineInterpolation(child);
|
||||
child = getDOM().nextSibling(child);
|
||||
this.left = new BaseLineIf(child);
|
||||
child = getDOM().nextSibling(child);
|
||||
this.right = new BaseLineIf(child);
|
||||
}
|
||||
update(value: TreeNode) {
|
||||
this.value.update(value.value);
|
||||
this.left.update(value.left);
|
||||
this.right.update(value.right);
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseLineInterpolation {
|
||||
value: string;
|
||||
constructor(public textNode: Node) {
|
||||
this.value = null;
|
||||
}
|
||||
update(value: string) {
|
||||
if (this.value !== value) {
|
||||
this.value = value;
|
||||
getDOM().setText(this.textNode, value + ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseLineIf {
|
||||
condition: boolean;
|
||||
component: BaseLineTreeComponent;
|
||||
constructor(public anchor: Node) {
|
||||
this.condition = false;
|
||||
this.component = null;
|
||||
}
|
||||
update(value: TreeNode) {
|
||||
var newCondition = !!value;
|
||||
if (this.condition !== newCondition) {
|
||||
this.condition = newCondition;
|
||||
if (this.component) {
|
||||
getDOM().remove(this.component.element);
|
||||
this.component = null;
|
||||
}
|
||||
if (this.condition) {
|
||||
var element = getDOM().firstChild((<any>getDOM().clone(BASELINE_IF_TEMPLATE)).content);
|
||||
this.anchor.parentNode.insertBefore(element, getDOM().nextSibling(this.anchor));
|
||||
this.component = new BaseLineTreeComponent(<HTMLElement>getDOM().firstChild(element));
|
||||
}
|
||||
}
|
||||
if (this.component) {
|
||||
this.component.update(value);
|
||||
}
|
||||
}
|
||||
}
|
27
modules/benchmarks/src/tree/baseline/index.html
Normal file
27
modules/benchmarks/src/tree/baseline/index.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Params</h2>
|
||||
<form>
|
||||
Depth:
|
||||
<input type="number" name="depth" placeholder="depth" value="9">
|
||||
<br>
|
||||
<button>Apply</button>
|
||||
</form>
|
||||
|
||||
<h2>Baseline tree benchmark</h2>
|
||||
<p>
|
||||
<button id="destroyDom">destroyDom</button>
|
||||
<button id="createDom">createDom</button>
|
||||
<button id="updateDomProfile">profile updateDom</button>
|
||||
<button id="createDomProfile">profile createDom</button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<baseline></baseline>
|
||||
</div>
|
||||
|
||||
<script src="../../bootstrap.js"></script>
|
||||
</body>
|
||||
</html>
|
35
modules/benchmarks/src/tree/baseline/index.ts
Normal file
35
modules/benchmarks/src/tree/baseline/index.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {
|
||||
bindAction
|
||||
} from '@angular/platform-browser/testing/benchmark_util';
|
||||
|
||||
import {BaseLineTreeComponent} from './app/tree';
|
||||
import {TreeNode, buildTree, emptyTree, profile} from '../app/util';
|
||||
import {BrowserDomAdapter} from '@angular/platform-browser/src/browser/browser_adapter';
|
||||
|
||||
export function main() {
|
||||
var app: BaseLineTreeComponent;
|
||||
|
||||
function destroyDom() { app.update(emptyTree()); }
|
||||
|
||||
function createDom() {
|
||||
app.update(buildTree());
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function init() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
const tree = document.createElement('tree');
|
||||
document.querySelector('baseline').appendChild(tree);
|
||||
app = new BaseLineTreeComponent(tree);
|
||||
|
||||
bindAction('#destroyDom', destroyDom);
|
||||
bindAction('#createDom', createDom);
|
||||
|
||||
bindAction('#updateDomProfile', profile(createDom, noop, 'baseline-update'));
|
||||
bindAction('#createDomProfile',
|
||||
profile(createDom, destroyDom, 'baseline-create'));
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
33
modules/benchmarks/src/tree/ng2/app/tree.ts
Normal file
33
modules/benchmarks/src/tree/ng2/app/tree.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
Component,
|
||||
NgModule
|
||||
} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
import {TreeNode, emptyTree} from '../../app/util';
|
||||
|
||||
@Component({
|
||||
selector: 'tree',
|
||||
inputs: ['data'],
|
||||
template:
|
||||
`<span> {{data.value}} <span template='ngIf data.right != null'><tree [data]='data.right'></tree></span><span template='ngIf data.left != null'><tree [data]='data.left'></tree></span></span>`
|
||||
})
|
||||
class TreeComponent {
|
||||
data: TreeNode;
|
||||
}
|
||||
|
||||
@Component(
|
||||
{selector: 'app', template: `<tree [data]='initData'></tree>`})
|
||||
export class AppComponent {
|
||||
initData: TreeNode;
|
||||
constructor() {
|
||||
this.initData = emptyTree();
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule],
|
||||
bootstrap: [AppComponent],
|
||||
declarations: [TreeComponent, AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
27
modules/benchmarks/src/tree/ng2/index.html
Normal file
27
modules/benchmarks/src/tree/ng2/index.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Params</h2>
|
||||
<form>
|
||||
Depth:
|
||||
<input type="number" name="depth" placeholder="depth" value="9">
|
||||
<br>
|
||||
<button>Apply</button>
|
||||
</form>
|
||||
|
||||
<h2>Angular2 tree benchmark</h2>
|
||||
<p>
|
||||
<button id="destroyDom">destroyDom</button>
|
||||
<button id="createDom">createDom</button>
|
||||
<button id="updateDomProfile">profile updateDom</button>
|
||||
<button id="createDomProfile">profile createDom</button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<app></app>
|
||||
</div>
|
||||
|
||||
<script src="../../bootstrap.js"></script>
|
||||
</body>
|
||||
</html>
|
49
modules/benchmarks/src/tree/ng2/index.ts
Normal file
49
modules/benchmarks/src/tree/ng2/index.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {
|
||||
NgModule,
|
||||
enableProdMode
|
||||
} from '@angular/core';
|
||||
|
||||
import {ApplicationRef} from '@angular/core/src/application_ref';
|
||||
import {
|
||||
bindAction,
|
||||
windowProfile,
|
||||
windowProfileEnd
|
||||
} from '@angular/platform-browser/testing/benchmark_util';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
|
||||
import {AppComponent, AppModule} from './app/tree';
|
||||
import {TreeNode, buildTree, emptyTree, profile} from '../app/util';
|
||||
|
||||
export function main() {
|
||||
var app: AppComponent;
|
||||
var appRef: ApplicationRef;
|
||||
|
||||
function destroyDom() {
|
||||
app.initData = emptyTree();
|
||||
appRef.tick();
|
||||
}
|
||||
|
||||
function createDom() {
|
||||
app.initData = buildTree();
|
||||
appRef.tick();
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function init() {
|
||||
enableProdMode();
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.then((ref) => {
|
||||
var injector = ref.injector;
|
||||
appRef = injector.get(ApplicationRef);
|
||||
|
||||
app = appRef.components[0].instance;
|
||||
bindAction('#destroyDom', destroyDom);
|
||||
bindAction('#createDom', createDom);
|
||||
bindAction('#updateDomProfile', profile(createDom, noop, 'ng2-update'));
|
||||
bindAction('#createDomProfile', profile(createDom, destroyDom, 'ng2-create'));
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
24
modules/benchmarks/src/tree/polymer/binary_tree.html
Normal file
24
modules/benchmarks/src/tree/polymer/binary_tree.html
Normal file
@ -0,0 +1,24 @@
|
||||
<link rel="import" href="/all/benchmarks/vendor/polymer/polymer.html">
|
||||
<dom-module id="binary-tree">
|
||||
<template>
|
||||
<span>
|
||||
<span>{{data.value}}</span>
|
||||
<template is="dom-if" if="[[data.left]]">
|
||||
<binary-tree data="[[data.left]]"></binary-tree>
|
||||
</template>
|
||||
<template is="dom-if" if="[[data.right]]">
|
||||
<binary-tree data="[[data.right]]"></binary-tree>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
</dom-module>
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'binary-tree',
|
||||
properties: {
|
||||
data: Object
|
||||
},
|
||||
leftTree: null,
|
||||
rightTree: null
|
||||
});
|
||||
</script>
|
29
modules/benchmarks/src/tree/polymer/index.html
Normal file
29
modules/benchmarks/src/tree/polymer/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="import" href="binary_tree.html">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>Params</h2>
|
||||
<form>
|
||||
Depth:
|
||||
<input type="number" name="depth" placeholder="depth" value="9">
|
||||
<br>
|
||||
<button>Apply</button>
|
||||
</form>
|
||||
|
||||
<h2>Polymer tree benchmark</h2>
|
||||
<p>
|
||||
<button id="destroyDom">destroyDom</button>
|
||||
<button id="createDom">createDom</button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<binary-tree id="app"></binary-tree>
|
||||
</div>
|
||||
|
||||
<script src="../../bootstrap.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
22
modules/benchmarks/src/tree/polymer/index.ts
Normal file
22
modules/benchmarks/src/tree/polymer/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {buildTree, emptyTree} from '../app/util';
|
||||
import {
|
||||
bindAction
|
||||
} from '@angular/platform-browser/testing/benchmark_util';
|
||||
|
||||
declare var Polymer: any;
|
||||
|
||||
export function main() {
|
||||
const rootEl: any = document.querySelector('binary-tree');
|
||||
rootEl.data = emptyTree();
|
||||
|
||||
function destroyDom() {
|
||||
rootEl.data = emptyTree();
|
||||
}
|
||||
|
||||
function createDom() {
|
||||
rootEl.data = buildTree();
|
||||
}
|
||||
|
||||
bindAction('#destroyDom', destroyDom);
|
||||
bindAction('#createDom', createDom);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Params</h2>
|
||||
<form>
|
||||
Depth:
|
||||
<input type="number" name="depth" placeholder="depth" value="9">
|
||||
<br>
|
||||
Use Viewcache:
|
||||
<label>
|
||||
Yes<input type="radio" name="viewcache" placeholder="use viewcache" value="true" checked="checked">
|
||||
</label>
|
||||
<label>
|
||||
No<input type="radio" name="viewcache" placeholder="use viewcache" value="false">
|
||||
</label>
|
||||
<br>
|
||||
<button>Apply</button>
|
||||
</form>
|
||||
|
||||
<h2>Angular2 tree benchmark</h2>
|
||||
<p>
|
||||
<button id="ng2DestroyDom">destroyDom</button>
|
||||
<button id="ng2CreateDom">createDom</button>
|
||||
<button id="ng2UpdateDomProfile">profile updateDom</button>
|
||||
<button id="ng2CreateDomProfile">profile createDom</button>
|
||||
</p>
|
||||
|
||||
<h2>Baseline tree benchmark</h2>
|
||||
<p>
|
||||
<button id="baselineDestroyDom">destroyDom</button>
|
||||
<button id="baselineCreateDom">createDom</button>
|
||||
<button id="baselineUpdateDomProfile">profile updateDom</button>
|
||||
<button id="baselineCreateDomProfile">profile createDom</button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<app></app>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<baseline></baseline>
|
||||
</div>
|
||||
|
||||
$SCRIPTS$
|
||||
</body>
|
||||
</html>
|
@ -1,246 +0,0 @@
|
||||
import {bootstrap} from '@angular/platform-browser';
|
||||
import {
|
||||
NgModule,
|
||||
Component,
|
||||
enableProdMode
|
||||
} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
import {ApplicationRef} from '@angular/core/src/application_ref';
|
||||
import {DOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {isPresent} from '@angular/facade/src/lang';
|
||||
import {window, document, gc} from '@angular/facade/src/browser';
|
||||
import {
|
||||
getIntParameter,
|
||||
getStringParameter,
|
||||
bindAction,
|
||||
windowProfile,
|
||||
windowProfileEnd
|
||||
} from '@angular/testing/src/benchmark_util';
|
||||
import {BrowserDomAdapter} from '@angular/platform-browser/src/browser/browser_adapter';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
|
||||
function createProviders(): any[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
var BASELINE_TREE_TEMPLATE;
|
||||
var BASELINE_IF_TEMPLATE;
|
||||
|
||||
export function main() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
enableProdMode();
|
||||
var maxDepth = getIntParameter('depth');
|
||||
|
||||
BASELINE_TREE_TEMPLATE = DOM.createTemplate(
|
||||
'<span>_<template class="ng-provider"></template><template class="ng-provider"></template></span>');
|
||||
BASELINE_IF_TEMPLATE = DOM.createTemplate('<span template="if"><tree></tree></span>');
|
||||
|
||||
var app;
|
||||
var appRef;
|
||||
var baselineRootTreeComponent;
|
||||
var count = 0;
|
||||
|
||||
function ng2DestroyDom() {
|
||||
// TODO: We need an initial value as otherwise the getter for data.value will fail
|
||||
// --> this should be already caught in change detection!
|
||||
app.initData = new TreeNode('', null, null);
|
||||
appRef.tick();
|
||||
}
|
||||
|
||||
function profile(create, destroy, name) {
|
||||
return function() {
|
||||
windowProfile(name + ' w GC');
|
||||
var duration = 0;
|
||||
var count = 0;
|
||||
while (count++ < 150) {
|
||||
gc();
|
||||
var start = window.performance.now();
|
||||
create();
|
||||
duration += window.performance.now() - start;
|
||||
destroy();
|
||||
}
|
||||
windowProfileEnd(name + ' w GC');
|
||||
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
|
||||
|
||||
windowProfile(name + ' w/o GC');
|
||||
duration = 0;
|
||||
count = 0;
|
||||
while (count++ < 150) {
|
||||
var start = window.performance.now();
|
||||
create();
|
||||
duration += window.performance.now() - start;
|
||||
destroy();
|
||||
}
|
||||
windowProfileEnd(name + ' w/o GC');
|
||||
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
|
||||
};
|
||||
}
|
||||
|
||||
function ng2CreateDom() {
|
||||
var values = count++ % 2 == 0 ? ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*'] :
|
||||
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', '-'];
|
||||
app.initData = buildTree(maxDepth, values, 0);
|
||||
appRef.tick();
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule],
|
||||
bootstrap: [AppComponent],
|
||||
providers: createProviders()
|
||||
})
|
||||
class AppModule {
|
||||
}
|
||||
|
||||
function initNg2() {
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.then((ref) => {
|
||||
var injector = ref.injector;
|
||||
appRef = injector.get(ApplicationRef);
|
||||
|
||||
app = ref.instance;
|
||||
bindAction('#ng2DestroyDom', ng2DestroyDom);
|
||||
bindAction('#ng2CreateDom', ng2CreateDom);
|
||||
bindAction('#ng2UpdateDomProfile', profile(ng2CreateDom, noop, 'ng2-update'));
|
||||
bindAction('#ng2CreateDomProfile', profile(ng2CreateDom, ng2DestroyDom, 'ng2-create'));
|
||||
});
|
||||
}
|
||||
|
||||
function baselineDestroyDom() { baselineRootTreeComponent.update(new TreeNode('', null, null)); }
|
||||
|
||||
function baselineCreateDom() {
|
||||
var values = count++ % 2 == 0 ? ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*'] :
|
||||
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', '-'];
|
||||
|
||||
baselineRootTreeComponent.update(buildTree(maxDepth, values, 0));
|
||||
}
|
||||
|
||||
function initBaseline() {
|
||||
var tree = DOM.createElement('tree');
|
||||
DOM.appendChild(DOM.querySelector(document, 'baseline'), tree);
|
||||
baselineRootTreeComponent = new BaseLineTreeComponent(tree);
|
||||
|
||||
bindAction('#baselineDestroyDom', baselineDestroyDom);
|
||||
bindAction('#baselineCreateDom', baselineCreateDom);
|
||||
|
||||
bindAction('#baselineUpdateDomProfile', profile(baselineCreateDom, noop, 'baseline-update'));
|
||||
bindAction('#baselineCreateDomProfile',
|
||||
profile(baselineCreateDom, baselineDestroyDom, 'baseline-create'));
|
||||
}
|
||||
|
||||
initNg2();
|
||||
initBaseline();
|
||||
}
|
||||
|
||||
class TreeNode {
|
||||
value: string;
|
||||
left: TreeNode;
|
||||
right: TreeNode;
|
||||
constructor(value, left, right) {
|
||||
this.value = value;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
}
|
||||
|
||||
function buildTree(maxDepth, values, curDepth) {
|
||||
if (maxDepth === curDepth) return new TreeNode('', null, null);
|
||||
return new TreeNode(values[curDepth], buildTree(maxDepth, values, curDepth + 1),
|
||||
buildTree(maxDepth, values, curDepth + 1));
|
||||
}
|
||||
|
||||
// http://jsperf.com/nextsibling-vs-childnodes
|
||||
|
||||
class BaseLineTreeComponent {
|
||||
element;
|
||||
value: BaseLineInterpolation;
|
||||
left: BaseLineIf;
|
||||
right: BaseLineIf;
|
||||
constructor(element) {
|
||||
this.element = element;
|
||||
var clone = DOM.clone(BASELINE_TREE_TEMPLATE.content.firstChild);
|
||||
var shadowRoot = this.element.createShadowRoot();
|
||||
DOM.appendChild(shadowRoot, clone);
|
||||
|
||||
var child = clone.firstChild;
|
||||
this.value = new BaseLineInterpolation(child);
|
||||
child = DOM.nextSibling(child);
|
||||
this.left = new BaseLineIf(child);
|
||||
child = DOM.nextSibling(child);
|
||||
this.right = new BaseLineIf(child);
|
||||
}
|
||||
update(value: TreeNode) {
|
||||
this.value.update(value.value);
|
||||
this.left.update(value.left);
|
||||
this.right.update(value.right);
|
||||
}
|
||||
}
|
||||
|
||||
class BaseLineInterpolation {
|
||||
value: string;
|
||||
textNode;
|
||||
constructor(textNode) {
|
||||
this.value = null;
|
||||
this.textNode = textNode;
|
||||
}
|
||||
update(value: string) {
|
||||
if (this.value !== value) {
|
||||
this.value = value;
|
||||
DOM.setText(this.textNode, value + ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BaseLineIf {
|
||||
condition: boolean;
|
||||
component: BaseLineTreeComponent;
|
||||
anchor;
|
||||
constructor(anchor) {
|
||||
this.anchor = anchor;
|
||||
this.condition = false;
|
||||
this.component = null;
|
||||
}
|
||||
update(value: TreeNode) {
|
||||
var newCondition = isPresent(value);
|
||||
if (this.condition !== newCondition) {
|
||||
this.condition = newCondition;
|
||||
if (isPresent(this.component)) {
|
||||
DOM.remove(this.component.element);
|
||||
this.component = null;
|
||||
}
|
||||
if (this.condition) {
|
||||
var element = DOM.firstChild((<any>DOM.clone(BASELINE_IF_TEMPLATE)).content);
|
||||
this.anchor.parentNode.insertBefore(element, DOM.nextSibling(this.anchor));
|
||||
this.component = new BaseLineTreeComponent(DOM.firstChild(element));
|
||||
}
|
||||
}
|
||||
if (isPresent(this.component)) {
|
||||
this.component.update(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'tree',
|
||||
inputs: ['data'],
|
||||
directives: [TreeComponent, NgIf],
|
||||
template:
|
||||
`<span> {{data.value}} <span template='ngIf data.right != null'><tree [data]='data.right'></tree></span><span template='ngIf data.left != null'><tree [data]='data.left'></tree></span></span>`
|
||||
})
|
||||
class TreeComponent {
|
||||
data: TreeNode;
|
||||
}
|
||||
|
||||
@Component(
|
||||
{selector: 'app', directives: [TreeComponent], template: `<tree [data]='initData'></tree>`})
|
||||
class AppComponent {
|
||||
initData: TreeNode;
|
||||
constructor() {
|
||||
// TODO: We need an initial value as otherwise the getter for data.value will fail
|
||||
// --> this should be already caught in change detection!
|
||||
this.initData = new TreeNode('', null, null);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user