From a29da2d1ce90db75f6e115eedcdd627df0b312b8 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Mon, 7 Aug 2017 18:10:27 +0300 Subject: [PATCH 01/20] refactor(renderer): keep internal sibling queue for views --- nativescript-angular/element-registry.ts | 10 ++- nativescript-angular/renderer.ts | 10 +-- nativescript-angular/view-util.ts | 87 +++++++++++++++--------- 3 files changed, 69 insertions(+), 38 deletions(-) diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts index 9a0f044aa..6461a3b9c 100644 --- a/nativescript-angular/element-registry.ts +++ b/nativescript-angular/element-registry.ts @@ -8,6 +8,9 @@ export interface ViewExtensions { nodeType: number; nodeName: string; templateParent: NgView; + previousSibling: NgElement; + nextSibling: NgElement; + lastChild: NgElement; ngCssClasses: Map; meta: ViewClassMeta; } @@ -22,6 +25,9 @@ export abstract class InvisibleNode extends View implements ViewExtensions { nodeType: number; nodeName: string; ngCssClasses: Map; + previousSibling: NgElement; + nextSibling: NgElement; + lastChild: NgElement; constructor() { super(); @@ -42,7 +48,7 @@ export class CommentNode extends InvisibleNode { super(); this.meta = { - skipAddToDom: false, + skipAddToDom: true, }; this.id = CommentNode.id.toString(); CommentNode.id += 1; @@ -67,7 +73,7 @@ const getClassName = instance => instance.constructor.name; export interface ViewClassMeta { skipAddToDom?: boolean; - insertChild?: (parent: NgView, child: NgView, atIndex: number) => void; + insertChild?: (parent: NgView, child: NgView, atIndex?: number) => void; removeChild?: (parent: NgView, child: NgView) => void; } diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index ddf247c0b..12b7669f0 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -13,7 +13,7 @@ import { profile } from "tns-core-modules/profiling"; import { APP_ROOT_VIEW, DEVICE, getRootPage } from "./platform-providers"; import { isBlank } from "./lang-facade"; import { ViewUtil } from "./view-util"; -import { NgView, InvisibleNode } from "./element-registry"; +import { NgView, NgElement, InvisibleNode } from "./element-registry"; import { rendererLog as traceLog } from "./trace"; // CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application. @@ -91,9 +91,9 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - insertBefore(parent: NgView, newChild: NgView, refChildIndex: number): void { + insertBefore(parent: NgView, newChild: NgView, refChild: NgElement): void { traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} parent: ${parent}`); - this.viewUtil.insertChild(parent, newChild, refChildIndex); + this.viewUtil.insertChild(parent, newChild, refChild); } @profile @@ -115,9 +115,9 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - nextSibling(node: NgView): number { + nextSibling(node: NgView): NgElement { traceLog(`NativeScriptRenderer.nextSibling ${node}`); - return this.viewUtil.nextSiblingIndex(node); + return node.nextSibling; } @profile diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 99b196197..30e1af30c 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -19,8 +19,8 @@ import { import { platformNames, Device } from "tns-core-modules/platform"; import { rendererLog as traceLog } from "./trace"; -const XML_ATTRIBUTES = Object.freeze(["style", "rows", "columns", "fontAttributes"]); const ELEMENT_NODE_TYPE = 1; +const XML_ATTRIBUTES = Object.freeze(["style", "rows", "columns", "fontAttributes"]); const whiteSpaceSplitter = /\s+/; export type ViewExtensions = ViewExtensions; @@ -53,37 +53,65 @@ export class ViewUtil { this.isAndroid = device.os === platformNames.android; } - public insertChild(parent: any, child: NgElement, atIndex: number = -1) { + public insertChild(parent: NgView, child: NgElement, refChild?: NgElement) { + // handle invisible nodes.. if (child instanceof InvisibleNode) { child.templateParent = parent; } + // add to queue + if (!parent) { + return; + } + const previousView = refChild || parent.lastChild; + if (previousView) { + previousView.nextSibling = child; + child.previousSibling = previousView; + } else { + parent.lastChild = child; + } + + // create actual view if (!parent || isDetachedElement(child)) { return; } if (parent.meta && parent.meta.insertChild) { - parent.meta.insertChild(parent, child, atIndex); + parent.meta.insertChild(parent, child); } else if (isLayout(parent)) { + // remove child if already exists if (child.parent === parent) { - const index = (parent).getChildIndex(child); + const index = parent.getChildIndex(child); if (index !== -1) { parent.removeChild(child); } } - if (atIndex !== -1) { + + // insert child + if (refChild) { + const atIndex = parent.getChildIndex(refChild); parent.insertChild(child, atIndex); } else { parent.addChild(child); } } else if (isContentView(parent)) { parent.content = child; - } else if (parent && parent._addChildFromBuilder) { - parent._addChildFromBuilder(child.nodeName, child); + } else if (parent && (parent)._addChildFromBuilder) { + (parent)._addChildFromBuilder(child.nodeName, child); } } - public removeChild(parent: any, child: NgElement) { + public removeChild(parent: NgView, child: NgElement) { + // remove from qeueue + if (child.previousSibling) { + child.previousSibling.nextSibling = child.nextSibling; + } + + if (child.nextSibling) { + child.nextSibling.previousSibling = child.previousSibling; + } + + // actual desctructuring if (!parent || isDetachedElement(child)) { return; } @@ -131,9 +159,7 @@ export class ViewUtil { // we're setting the node type of the view // to 'element' because of checks done in the - // dom animation engine: - // tslint:disable-next-line:max-line-length - // https://github.com/angular/angular/blob/master/packages/animations/browser/src/render/dom_animation_engine.ts#L70-L81 + // dom animation engine view.nodeType = ELEMENT_NODE_TYPE; return view; @@ -170,32 +196,31 @@ export class ViewUtil { // finds the node in the parent's views and returns the next index // returns -1 if the node has no parent or next sibling - public nextSiblingIndex(node: NgView): number { - const parent = node.parent; - if (!parent) { - return -1; - } - - let index = 0; - let found = false; - parent.eachChild(child => { - if (child === node) { - found = true; - } - - index += 1; - return !found; - }); - - return found ? index : -1; - } + // public nextSiblingIndex(node: NgView): number { + // const parent = node.parent; + // if (!parent) { + // return -1; + // } + + // let index = 0; + // let found = false; + // parent.eachChild(child => { + // if (child === node) { + // found = true; + // } + + // index += 1; + // return !found; + // }); + + // return found ? index : -1; + // } private runsIn(platform: string): boolean { return (platform === "ios" && this.isIos) || (platform === "android" && this.isAndroid); } - private setPropertyInternal(view: NgView, attributeName: string, value: any): void { traceLog(`Setting attribute: ${attributeName}=${value} to ${view}`); From 9e2fcaa95e4ee571ba5248aa1ca424643b3fdb5b Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Mon, 7 Aug 2017 18:14:16 +0300 Subject: [PATCH 02/20] test: update test interface --- tests/app/tests/property-sets.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/app/tests/property-sets.ts b/tests/app/tests/property-sets.ts index e62bb8c74..e0366f401 100644 --- a/tests/app/tests/property-sets.ts +++ b/tests/app/tests/property-sets.ts @@ -6,12 +6,16 @@ import { NgView, ViewExtensions, ViewClassMeta, + NgElement, } from "nativescript-angular/element-registry"; import {Red} from "color/known-colors"; import {device, platformNames} from "platform"; import {createDevice} from "./test-utils"; class TestView extends View implements ViewExtensions { + public previousSibling: NgElement; + public nextSibling: NgElement; + public lastChild: NgElement; public nodeName: string = "TestView"; public nodeType: number = 1; public templateParent: NgView = null; From 1520aecf96c90cbfcf7786a5864bc552994919fa Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Sat, 12 Aug 2017 14:23:47 +0300 Subject: [PATCH 03/20] refactor(renderer): fix setting of siblings --- nativescript-angular/renderer.ts | 5 +++-- nativescript-angular/view-util.ts | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 12b7669f0..90b87f370 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -92,7 +92,8 @@ export class NativeScriptRenderer extends Renderer2 { @profile insertBefore(parent: NgView, newChild: NgView, refChild: NgElement): void { - traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} parent: ${parent}`); + traceLog(`NativeScriptRenderer.insertBefore ` + + `child: ${newChild} parent: ${parent} refChild: ${refChild}`); this.viewUtil.insertChild(parent, newChild, refChild); } @@ -110,7 +111,7 @@ export class NativeScriptRenderer extends Renderer2 { @profile parentNode(node: NgView): any { - traceLog("NativeScriptRenderer.parentNode for node: " + node); + traceLog(`NativeScriptRenderer.parentNode for node: ${node}`); return node.parent || node.templateParent; } diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 30e1af30c..51281db8e 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -60,14 +60,14 @@ export class ViewUtil { } // add to queue - if (!parent) { - return; - } - const previousView = refChild || parent.lastChild; + const previousView = refChild || (parent && parent.lastChild); if (previousView) { previousView.nextSibling = child; child.previousSibling = previousView; - } else { + } + + // TODO: check number of parent's children + if (!refChild && parent) { parent.lastChild = child; } From 063fe727ecf5591466f040eb40be7c23193433af Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Sat, 12 Aug 2017 15:10:09 +0300 Subject: [PATCH 04/20] refactor(renderer): skip invisible nodes when using insertBefore --- nativescript-angular/view-util.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 51281db8e..e5f693a8b 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -66,11 +66,17 @@ export class ViewUtil { child.previousSibling = previousView; } - // TODO: check number of parent's children if (!refChild && parent) { parent.lastChild = child; } + // skip invisible elements ... + while (isDetachedElement(refChild)) { + refChild = refChild.previousSibling; + previousView.nextSibling = child; + child.previousSibling = previousView; + } + // create actual view if (!parent || isDetachedElement(child)) { return; @@ -88,6 +94,7 @@ export class ViewUtil { } // insert child + if (refChild) { const atIndex = parent.getChildIndex(refChild); parent.insertChild(child, atIndex); From ee29397491b8872b82ef03d859b3442b239e1cf9 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Sat, 12 Aug 2017 18:11:22 +0300 Subject: [PATCH 05/20] refactor(renderer): replace doubly linked list with singly linked one --- nativescript-angular/element-registry.ts | 8 ++- nativescript-angular/renderer.ts | 26 +++++++--- nativescript-angular/view-util.ts | 63 +++++++++++++----------- 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts index 6461a3b9c..e50a9ee89 100644 --- a/nativescript-angular/element-registry.ts +++ b/nativescript-angular/element-registry.ts @@ -8,13 +8,17 @@ export interface ViewExtensions { nodeType: number; nodeName: string; templateParent: NgView; - previousSibling: NgElement; nextSibling: NgElement; lastChild: NgElement; ngCssClasses: Map; meta: ViewClassMeta; } +export interface ElementReference { + previous: NgElement; + next: NgElement; +} + export interface ViewClass { new (): View; } @@ -73,7 +77,7 @@ const getClassName = instance => instance.constructor.name; export interface ViewClassMeta { skipAddToDom?: boolean; - insertChild?: (parent: NgView, child: NgView, atIndex?: number) => void; + insertChild?: (parent: NgView, child: NgView) => void; removeChild?: (parent: NgView, child: NgView) => void; } diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 90b87f370..a83c518db 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -13,7 +13,7 @@ import { profile } from "tns-core-modules/profiling"; import { APP_ROOT_VIEW, DEVICE, getRootPage } from "./platform-providers"; import { isBlank } from "./lang-facade"; import { ViewUtil } from "./view-util"; -import { NgView, NgElement, InvisibleNode } from "./element-registry"; +import { NgView, NgElement, InvisibleNode, ElementReference, isDetachedElement } from "./element-registry"; import { rendererLog as traceLog } from "./trace"; // CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application. @@ -23,6 +23,7 @@ export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`; export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`; const ATTR_SANITIZER = /-/g; + @Injectable() export class NativeScriptRendererFactory implements RendererFactory2 { private componentRenderers = new Map(); @@ -91,10 +92,10 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - insertBefore(parent: NgView, newChild: NgView, refChild: NgElement): void { - traceLog(`NativeScriptRenderer.insertBefore ` + - `child: ${newChild} parent: ${parent} refChild: ${refChild}`); - this.viewUtil.insertChild(parent, newChild, refChild); + insertBefore(parent: NgView, newChild: NgView, { previous, next }: ElementReference): void { + traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} ` + + `parent: ${parent} previous: ${previous} next: ${next}`); + this.viewUtil.insertChild(parent, newChild, previous, next); } @profile @@ -116,9 +117,18 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - nextSibling(node: NgView): NgElement { - traceLog(`NativeScriptRenderer.nextSibling ${node}`); - return node.nextSibling; + nextSibling(node: NgView): ElementReference { + traceLog(`NativeScriptRenderer.nextSibling of ${node} is ${node.nextSibling}`); + + let next = node.nextSibling; + while (next && isDetachedElement(next)) { + next = next.nextSibling; + } + + return { + previous: node, + next, + }; } @profile diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index e5f693a8b..9e694410f 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -5,6 +5,7 @@ import { ContentView } from "tns-core-modules/ui/content-view"; import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; import { CommentNode, + ElementReference, InvisibleNode, NgElement, NgView, @@ -53,35 +54,35 @@ export class ViewUtil { this.isAndroid = device.os === platformNames.android; } - public insertChild(parent: NgView, child: NgElement, refChild?: NgElement) { + public insertChild(parent: NgView, child: NgElement, previous?: NgElement, next?: NgElement) { // handle invisible nodes.. - if (child instanceof InvisibleNode) { - child.templateParent = parent; + if (!parent) { + return; } - // add to queue - const previousView = refChild || (parent && parent.lastChild); - if (previousView) { - previousView.nextSibling = child; - child.previousSibling = previousView; + if (child instanceof InvisibleNode) { + child.templateParent = parent; } - if (!refChild && parent) { + // add to end of queue if no next + if (parent && !next) { + if (parent.lastChild) { + parent.lastChild.nextSibling = child; + } parent.lastChild = child; } - // skip invisible elements ... - while (isDetachedElement(refChild)) { - refChild = refChild.previousSibling; - previousView.nextSibling = child; - child.previousSibling = previousView; + // update queue if there is next + if (next) { + previous.nextSibling = child; + child.nextSibling = next; } - // create actual view - if (!parent || isDetachedElement(child)) { + if (isDetachedElement(child)) { return; } + // create actual view if (parent.meta && parent.meta.insertChild) { parent.meta.insertChild(parent, child); } else if (isLayout(parent)) { @@ -95,12 +96,13 @@ export class ViewUtil { // insert child - if (refChild) { - const atIndex = parent.getChildIndex(refChild); + if (next) { + const atIndex = parent.getChildIndex(next); parent.insertChild(child, atIndex); } else { parent.addChild(child); } + } else if (isContentView(parent)) { parent.content = child; } else if (parent && (parent)._addChildFromBuilder) { @@ -109,29 +111,34 @@ export class ViewUtil { } public removeChild(parent: NgView, child: NgElement) { - // remove from qeueue - if (child.previousSibling) { - child.previousSibling.nextSibling = child.nextSibling; - } - - if (child.nextSibling) { - child.nextSibling.previousSibling = child.previousSibling; - } - - // actual desctructuring if (!parent || isDetachedElement(child)) { + console.log("skip the removal of detached element") return; } if (parent.meta && parent.meta.removeChild) { parent.meta.removeChild(parent, child); } else if (isLayout(parent)) { + console.log("remove child from layout") + const atIndex = parent.getChildIndex(child); + if (atIndex === -1) { + return; + } + + if (atIndex !== 0) { + const previous = parent.getChildAt(atIndex - 1) as NgElement; + previous.nextSibling = child.nextSibling; + } + parent.removeChild(child); + } else if (isContentView(parent)) { + console.log("remove child from content view"); if (parent.content === child) { parent.content = null; } } else if (isView(parent)) { + console.log("remove child from view element"); parent._removeView(child); } } From 0bc7d77e1c936bc76c979a3ac26d43bd4c7dc954 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Wed, 16 Aug 2017 13:47:01 +0300 Subject: [PATCH 06/20] refactor(renderer): split insert/remove of views to smaller methods methods for insert/remove from: - internal view queue (NgElements) - visual tree (NgViews) --- nativescript-angular/view-util.ts | 127 +++++++++++++++++------------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 9e694410f..2a39426ba 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -55,54 +55,49 @@ export class ViewUtil { } public insertChild(parent: NgView, child: NgElement, previous?: NgElement, next?: NgElement) { - // handle invisible nodes.. if (!parent) { return; } - if (child instanceof InvisibleNode) { - child.templateParent = parent; - } + this.addToQueue(parent, child, previous, next); - // add to end of queue if no next - if (parent && !next) { - if (parent.lastChild) { - parent.lastChild.nextSibling = child; - } - parent.lastChild = child; + if (isDetachedElement(child)) { + child.templateParent = parent; + } else { + this.addToVisualTree(parent, child, next); } + } - // update queue if there is next + private addToQueue(parent: NgElement, child: NgElement, previous: NgElement, next: NgElement) { if (next) { - previous.nextSibling = child; - child.nextSibling = next; + this.insertBetween(child, previous, next); + } else { + this.appendToQueue(parent, child); } + } - if (isDetachedElement(child)) { - return; + private insertBetween( + view: NgElement, + previous: NgElement, + next: NgElement + ): void { + previous.nextSibling = view; + view.nextSibling = next; + } + + private appendToQueue(parent: NgElement, view: NgElement) { + if (parent.lastChild) { + parent.lastChild.nextSibling = view; } - // create actual view + parent.lastChild = view; + } + + private addToVisualTree(parent: NgView, child: NgView, next: NgView): void { if (parent.meta && parent.meta.insertChild) { parent.meta.insertChild(parent, child); } else if (isLayout(parent)) { - // remove child if already exists - if (child.parent === parent) { - const index = parent.getChildIndex(child); - if (index !== -1) { - parent.removeChild(child); - } - } - - // insert child - - if (next) { - const atIndex = parent.getChildIndex(next); - parent.insertChild(child, atIndex); - } else { - parent.addChild(child); - } - + this.insertToLayout(parent, child, next); } else if (isContentView(parent)) { parent.content = child; } else if (parent && (parent)._addChildFromBuilder) { @@ -110,37 +105,63 @@ export class ViewUtil { } } + private insertToLayout( + parent: NgLayoutBase, + child: NgView, + next: NgView + ): void { + if (child.parent === parent) { + this.removeLayoutChild(parent, child); + } + + if (next) { + const index = parent.getChildIndex(next); + parent.insertChild(child, index); + } else { + parent.addChild(child); + } + } + public removeChild(parent: NgView, child: NgElement) { - if (!parent || isDetachedElement(child)) { - console.log("skip the removal of detached element") + if (!parent) { return; } if (parent.meta && parent.meta.removeChild) { parent.meta.removeChild(parent, child); } else if (isLayout(parent)) { - console.log("remove child from layout") - const atIndex = parent.getChildIndex(child); - if (atIndex === -1) { - return; - } + this.removeLayoutChild(parent, child); + } else if (isContentView(parent) && parent.content === child) { + parent.content = null; + } else if (isView(parent)) { + parent._removeView(child); + } + } - if (atIndex !== 0) { - const previous = parent.getChildAt(atIndex - 1) as NgElement; - previous.nextSibling = child.nextSibling; - } + private removeLayoutChild(parent: NgLayoutBase, child: NgView): void { + const index = parent.getChildIndex(child); + if (index === -1) { + return; + } - parent.removeChild(child); + this.removeFromQueue(parent, child, index); + parent.removeChild(child); + } - } else if (isContentView(parent)) { - console.log("remove child from content view"); - if (parent.content === child) { - parent.content = null; - } - } else if (isView(parent)) { - console.log("remove child from view element"); - parent._removeView(child); + private removeFromQueue(parent: NgLayoutBase, child: NgView, index: number) { + let previous = parent.getChildAt(index - 1) as NgElement; + if (!previous) { + return; } + + // since detached elements are not added to the visual tree, + // we need to find the actual previous sibling of the view, + // which may be an invisible node + while (previous && previous !== child && isDetachedElement(previous.nextSibling)) { + previous = previous.nextSibling; + } + + previous.nextSibling = child.nextSibling; } public getChildIndex(parent: any, child: NgView) { From 4c8dd799f0cc7ec2dcfec2339145307cb530ada8 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Wed, 16 Aug 2017 13:50:22 +0300 Subject: [PATCH 07/20] refactor(renderer): set first child to every NgElement --- nativescript-angular/element-registry.ts | 9 ++-- nativescript-angular/view-util.ts | 53 +++++++++++++++--------- tests/app/tests/property-sets.ts | 10 ++--- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts index e50a9ee89..d9715d880 100644 --- a/nativescript-angular/element-registry.ts +++ b/nativescript-angular/element-registry.ts @@ -5,13 +5,14 @@ export type NgView = (View & ViewExtensions); export type NgElement = NgView | InvisibleNode; export interface ViewExtensions { + meta: ViewClassMeta; nodeType: number; nodeName: string; templateParent: NgView; nextSibling: NgElement; + firstChild: NgElement; lastChild: NgElement; ngCssClasses: Map; - meta: ViewClassMeta; } export interface ElementReference { @@ -25,13 +26,13 @@ export interface ViewClass { export abstract class InvisibleNode extends View implements ViewExtensions { meta: { skipAddToDom: boolean }; - templateParent: NgView; nodeType: number; nodeName: string; - ngCssClasses: Map; - previousSibling: NgElement; + templateParent: NgView; nextSibling: NgElement; + firstChild: NgElement; lastChild: NgElement; + ngCssClasses: Map; constructor() { super(); diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index 2a39426ba..baba92a3e 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -54,7 +54,12 @@ export class ViewUtil { this.isAndroid = device.os === platformNames.android; } - public insertChild(parent: NgView, child: NgElement, previous?: NgElement, next?: NgElement) { + public insertChild( + parent: NgView, + child: NgElement, + previous: NgElement = parent.lastChild, + next?: NgElement + ) { if (!parent) { return; } @@ -68,23 +73,25 @@ export class ViewUtil { } } - private addToQueue(parent: NgElement, child: NgElement, previous: NgElement, next: NgElement) { + private addToQueue( + parent: NgElement, + child: NgElement, + previous: NgElement, + next: NgElement + ): void { + if (previous) { + previous.nextSibling = child; + } else { + parent.firstChild = child; + } + if (next) { - this.insertBetween(child, previous, next); + child.nextSibling = next; } else { this.appendToQueue(parent, child); } } - private insertBetween( - view: NgElement, - previous: NgElement, - next: NgElement - ): void { - previous.nextSibling = view; - view.nextSibling = next; - } - private appendToQueue(parent: NgElement, view: NgElement) { if (parent.lastChild) { parent.lastChild.nextSibling = view; @@ -133,6 +140,8 @@ export class ViewUtil { this.removeLayoutChild(parent, child); } else if (isContentView(parent) && parent.content === child) { parent.content = null; + parent.lastChild = null; + parent.firstChild = null; } else if (isView(parent)) { parent._removeView(child); } @@ -140,28 +149,34 @@ export class ViewUtil { private removeLayoutChild(parent: NgLayoutBase, child: NgView): void { const index = parent.getChildIndex(child); + this.removeFromQueue(parent, child, index); if (index === -1) { return; } - this.removeFromQueue(parent, child, index); parent.removeChild(child); } private removeFromQueue(parent: NgLayoutBase, child: NgView, index: number) { - let previous = parent.getChildAt(index - 1) as NgElement; - if (!previous) { - return; + if (parent.firstChild === child) { + parent.firstChild = child.nextSibling; + } + + let previous = (parent.getChildAt(index - 1) || parent.firstChild) as NgElement; + if (parent.lastChild === child) { + parent.lastChild = previous; } // since detached elements are not added to the visual tree, // we need to find the actual previous sibling of the view, - // which may be an invisible node - while (previous && previous !== child && isDetachedElement(previous.nextSibling)) { + // which may as well be an invisible node + while ( previous && previous !== child && isDetachedElement(previous.nextSibling)) { previous = previous.nextSibling; } - previous.nextSibling = child.nextSibling; + if (previous) { + previous.nextSibling = child.nextSibling; + } } public getChildIndex(parent: any, child: NgView) { diff --git a/tests/app/tests/property-sets.ts b/tests/app/tests/property-sets.ts index e0366f401..c5dbf76fa 100644 --- a/tests/app/tests/property-sets.ts +++ b/tests/app/tests/property-sets.ts @@ -13,13 +13,13 @@ import {device, platformNames} from "platform"; import {createDevice} from "./test-utils"; class TestView extends View implements ViewExtensions { - public previousSibling: NgElement; - public nextSibling: NgElement; - public lastChild: NgElement; - public nodeName: string = "TestView"; + public meta: ViewClassMeta = { skipAddToDom: false }; public nodeType: number = 1; + public nodeName: string = "TestView"; public templateParent: NgView = null; - public meta: ViewClassMeta = { skipAddToDom: false }; + public nextSibling: NgElement; + public firstChild: NgElement; + public lastChild: NgElement; public ngCssClasses: Map = new Map(); public stringValue: string = ""; From 580d1c1517da4ed6388e5368c0bc2b47cf41518c Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Wed, 16 Aug 2017 15:11:42 +0300 Subject: [PATCH 08/20] style: fix linting errors --- nativescript-angular/renderer.ts | 36 +++++++++++++++---------------- nativescript-angular/view-util.ts | 1 - 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index a83c518db..5527af4aa 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -77,7 +77,7 @@ export class NativeScriptRenderer extends Renderer2 { data: { [key: string]: any } = Object.create(null); constructor( - private rootView: NgView, + private rootView: NgElement, private zone: NgZone, private viewUtil: ViewUtil ) { @@ -86,20 +86,20 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - appendChild(parent: any, newChild: NgView): void { + appendChild(parent: NgElement, newChild: NgElement): void { traceLog(`NativeScriptRenderer.appendChild child: ${newChild} parent: ${parent}`); this.viewUtil.insertChild(parent, newChild); } @profile - insertBefore(parent: NgView, newChild: NgView, { previous, next }: ElementReference): void { + insertBefore(parent: NgElement, newChild: NgElement, { previous, next }: ElementReference): void { traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} ` + `parent: ${parent} previous: ${previous} next: ${next}`); this.viewUtil.insertChild(parent, newChild, previous, next); } @profile - removeChild(parent: any, oldChild: NgView): void { + removeChild(parent: any, oldChild: NgElement): void { traceLog(`NativeScriptRenderer.removeChild child: ${oldChild} parent: ${parent}`); this.viewUtil.removeChild(parent, oldChild); } @@ -117,7 +117,7 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - nextSibling(node: NgView): ElementReference { + nextSibling(node: NgElement): ElementReference { traceLog(`NativeScriptRenderer.nextSibling of ${node} is ${node.nextSibling}`); let next = node.nextSibling; @@ -138,7 +138,7 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - createElement(name: any, _namespace: string): NgView { + createElement(name: any, _namespace: string): NgElement { traceLog(`NativeScriptRenderer.createElement: ${name}`); return this.viewUtil.createView(name); } @@ -156,7 +156,7 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - projectNodes(parentElement: NgView, nodes: NgView[]): void { + projectNodes(parentElement: NgElement, nodes: NgElement[]): void { traceLog("NativeScriptRenderer.projectNodes"); nodes.forEach((node) => this.viewUtil.insertChild(parentElement, node)); } @@ -169,13 +169,13 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - setAttribute(view: NgView, name: string, value: string, namespace?: string) { + setAttribute(view: NgElement, name: string, value: string, namespace?: string) { traceLog(`NativeScriptRenderer.setAttribute ${view} : ${name} = ${value}, namespace: ${namespace}`); return this.viewUtil.setProperty(view, name, value, namespace); } @profile - removeAttribute(_el: NgView, _name: string): void { + removeAttribute(_el: NgElement, _name: string): void { traceLog(`NativeScriptRenderer.removeAttribute ${_el}: ${_name}`); } @@ -186,25 +186,25 @@ export class NativeScriptRenderer extends Renderer2 { } @profile - addClass(view: NgView, name: string): void { + addClass(view: NgElement, name: string): void { traceLog(`NativeScriptRenderer.addClass ${name}`); this.viewUtil.addClass(view, name); } @profile - removeClass(view: NgView, name: string): void { + removeClass(view: NgElement, name: string): void { traceLog(`NativeScriptRenderer.removeClass ${name}`); this.viewUtil.removeClass(view, name); } @profile - setStyle(view: NgView, styleName: string, value: any, _flags?: RendererStyleFlags2): void { + setStyle(view: NgElement, styleName: string, value: any, _flags?: RendererStyleFlags2): void { traceLog(`NativeScriptRenderer.setStyle: ${styleName} = ${value}`); this.viewUtil.setStyle(view, styleName, value); } @profile - removeStyle(view: NgView, styleName: string, _flags?: RendererStyleFlags2): void { + removeStyle(view: NgElement, styleName: string, _flags?: RendererStyleFlags2): void { traceLog("NativeScriptRenderer.removeStyle: ${styleName}"); this.viewUtil.removeStyle(view, styleName); } @@ -212,7 +212,7 @@ export class NativeScriptRenderer extends Renderer2 { // Used only in debug mode to serialize property changes to comment nodes, // such as