From 05260b789168b39b1a092f60e931c789381882b5 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Wed, 3 May 2017 18:23:54 +0300 Subject: [PATCH 1/7] fix(renderer): set templateParent for newly create DetachedText nodes DetachedText is not inserted in the UI components tree, so we need to add it's parent manually. --- nativescript-angular/view-util.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index a395e961f..c8b816ca0 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -48,7 +48,12 @@ export class ViewUtil { } public insertChild(parent: any, child: NgView, atIndex: number = -1) { - if (!parent || child.meta.skipAddToDom) { + if (!parent) { + return; + } + + if (child.meta.skipAddToDom) { + child.templateParent = parent; return; } From 6dd8b18204885420950408d8a81d87ead8e1fc16 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Wed, 3 May 2017 18:25:48 +0300 Subject: [PATCH 2/7] fix(renderer): respect templateParent in parentNode() if one is set fixes #777, fixes #787 --- nativescript-angular/renderer.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index 9773b0bf9..ccc6be962 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -43,6 +43,7 @@ export class NativeScriptRendererFactory implements RendererFactory2 { if (!rootView) { rootView = getRootPage() || topmost().currentPage; } + rootView.nodeName = "NONE"; this.rootNgView = rootView; } @@ -83,26 +84,17 @@ export class NativeScriptRenderer extends Renderer2 { appendChild(parent: any, newChild: NgView): void { traceLog(`NativeScriptRenderer.appendChild child: ${newChild} parent: ${parent}`); - - if (parent) { - this.viewUtil.insertChild(parent, newChild); - } + this.viewUtil.insertChild(parent, newChild); } insertBefore(parent: NgView, newChild: NgView, refChildIndex: number): void { traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} parent: ${parent}`); - - if (parent) { - this.viewUtil.insertChild(parent, newChild, refChildIndex); - } + this.viewUtil.insertChild(parent, newChild, refChildIndex); } removeChild(parent: any, oldChild: NgView): void { traceLog(`NativeScriptRenderer.removeChild child: ${oldChild} parent: ${parent}`); - - if (parent) { - this.viewUtil.removeChild(parent, oldChild); - } + this.viewUtil.removeChild(parent, oldChild); } selectRootElement(selector: string): NgView { @@ -111,7 +103,7 @@ export class NativeScriptRenderer extends Renderer2 { } parentNode(node: NgView): any { - return node.parent; + return node.parent || node.templateParent; } nextSibling(node: NgView): number { From bff31dc5cd6489694b2d9695d8e40410fde3e5a2 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Thu, 4 May 2017 20:03:42 +0300 Subject: [PATCH 3/7] test: add unit test for NgIfElse and NgIfThenElse --- tests/app/tests/renderer-tests.ts | 114 +++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index 779c79cee..fa5e34531 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -94,6 +94,48 @@ export class NgIfLabel { } } +@Component({ + selector: "ng-if-else", + template: ` + + + + + + + + ` +}) +export class NgIfElseComponent { + public show: boolean = true; + constructor(public elementRef: ElementRef) { + } +} + + +@Component({ + selector: "ng-if-then-else", + template: ` + + + + + + + + + + + + + ` +}) +export class NgIfThenElseComponent { + public show: boolean = true; + constructor(public elementRef: ElementRef) { + } +} + @Component({ selector: "ng-for-label", template: `` @@ -112,10 +154,10 @@ describe("Renderer E2E", () => { LayoutWithLabel, LabelCmp, LabelContainer, ProjectableCmp, ProjectionContainer, StyledLabelCmp, StyledLabelCmp2, - NgIfLabel, NgForLabel, + NgIfLabel, NgIfElseComponent, NgIfThenElseComponent, + NgForLabel, ]).then((app) => { testApp = app; - }); }); @@ -229,6 +271,74 @@ describe("Renderer E2E", () => { }); }); + it("ngIfElse show 'if' template when condition is true", () => { + return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + testApp.appRef.tick(); + assert.equal( + "(ProxyViewContainer " + + "(StackLayout " + + "(#comment), " + // ng-reflect comment + "(Label[text=If]), (#comment)))", // the content to be displayed and its anchor + + dumpView(componentRoot, true)); + }); + }); + + it("ngIfElse show 'else' template when condition is false", () => { + return testApp.loadComponent(NgIfElseComponent).then(componentRef => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + component.show = false; + testApp.appRef.tick(); + assert.equal( + "(ProxyViewContainer " + + "(StackLayout " + + "(#comment), " + // ng-reflect comment + "(Label[text=Else]), (#comment)))", // the content to be displayed and its anchor + + dumpView(componentRoot, true)); + }); + }); + + it("ngIfThenElse show 'then' template when condition is true", () => { + return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + testApp.appRef.tick(); + assert.equal( + "(ProxyViewContainer " + + "(StackLayout " + + "(#comment), " + // ng-reflect comment + "(Label[text=Then]), (#comment), " + // the content to be displayed and its anchor + "(#comment)))", // the anchor for the else template + dumpView(componentRoot, true)); + }); + }); + + + it("ngIfThenElse show 'else' template when condition is false", () => { + return testApp.loadComponent(NgIfThenElseComponent).then(componentRef => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + component.show = false; + testApp.appRef.tick(); + assert.equal( + "(ProxyViewContainer " + + "(StackLayout " + + "(#comment), " + // ng-reflect comment + "(Label[text=Else]), (#comment), " + // the content to be displayed and its anchor + "(#comment)))", // the anchor for the else template + + dumpView(componentRoot, true)); + }); + }); + it("ngFor creates element for each item", () => { return testApp.loadComponent(NgForLabel).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; From 5fc9f86cb5d31cab6e2cbd0649a0d112548e9059 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Thu, 4 May 2017 20:04:55 +0300 Subject: [PATCH 4/7] chore: target 'next' tag of tns-core-modules --- nativescript-angular/package.json | 2 +- ng-sample/package.json | 9 ++++----- tests/package.json | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index 8f8abf671..fda01e910 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -66,7 +66,7 @@ "@angular/router": "~4.0.0 || ~4.1.0", "codelyzer": "^3.0.1", "rxjs": "^5.0.1", - "tns-core-modules": "internal-preview", + "tns-core-modules": "next", "tslint": "^5.1.0", "typescript": "^2.3.2", "zone.js": "^0.8.4" diff --git a/ng-sample/package.json b/ng-sample/package.json index 983f3d3a2..c04b4319c 100644 --- a/ng-sample/package.json +++ b/ng-sample/package.json @@ -2,10 +2,10 @@ "nativescript": { "id": "org.nativescript.ngsample", "tns-android": { - "version": "2.5.0" + "version": "3.0.0" }, "tns-ios": { - "version": "2.5.0" + "version": "3.0.0" } }, "name": "tns-template-hello-world", @@ -43,7 +43,7 @@ "@angular/router": "~4.1.0", "nativescript-angular": "file:../nativescript-angular", "rxjs": "^5.3.0", - "tns-core-modules": "^3.0.0 || ^3.0.0-rc.1", + "tns-core-modules": "next", "zone.js": "~0.8.2" }, "devDependencies": { @@ -53,10 +53,9 @@ "filewalker": "0.1.2", "lazy": "1.0.11", "nativescript-dev-typescript": "^0.3.1", - "nativescript-dev-webpack": "0.0.13", "shelljs": "^0.7.0", "tslint": "^4.5.1", - "typescript": "~2.2.0" + "typescript": "~2.3.2" }, "scripts": { "tslint": "tslint --project tsconfig.json --config tslint.json" diff --git a/tests/package.json b/tests/package.json index fb8cbbd5c..0b894576e 100644 --- a/tests/package.json +++ b/tests/package.json @@ -38,7 +38,7 @@ "nativescript-angular": "../nativescript-angular", "nativescript-unit-test-runner": "^0.3.4", "rxjs": "^5.2.0", - "tns-core-modules": "internal-preview", + "tns-core-modules": "next", "zone.js": "^0.8.2" }, "devDependencies": { From f1d9e054034108fc93965b2bc401a097956f15f0 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Fri, 5 May 2017 11:31:02 +0300 Subject: [PATCH 5/7] fix(renderer): stop attaching comments to visual tree --- nativescript-angular/element-registry.ts | 2 +- tests/app/tests/renderer-tests.ts | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts index 534377b0c..755005f95 100644 --- a/nativescript-angular/element-registry.ts +++ b/nativescript-angular/element-registry.ts @@ -115,5 +115,5 @@ registerElement("DetachedText", () => require("ui/placeholder").Placeholder, { skipAddToDom: true }); registerElement("Comment", () => require("ui/placeholder").Placeholder, - { skipAddToDom: false }); + { skipAddToDom: true }); diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index fa5e34531..fecbaf982 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -256,7 +256,7 @@ describe("Renderer E2E", () => { it("ngIf hides component when false", () => { return testApp.loadComponent(NgIfLabel).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; - assert.equal("(ProxyViewContainer (#comment))", dumpView(componentRoot)); + assert.equal("(ProxyViewContainer)", dumpView(componentRoot)); }); }); @@ -267,7 +267,7 @@ describe("Renderer E2E", () => { component.show = true; testApp.appRef.tick(); - assert.equal("(ProxyViewContainer (#comment), (Label))", dumpView(componentRoot)); + assert.equal("(ProxyViewContainer (Label))", dumpView(componentRoot)); }); }); @@ -280,8 +280,7 @@ describe("Renderer E2E", () => { assert.equal( "(ProxyViewContainer " + "(StackLayout " + - "(#comment), " + // ng-reflect comment - "(Label[text=If]), (#comment)))", // the content to be displayed and its anchor + "(Label[text=If])))", // the content to be displayed dumpView(componentRoot, true)); }); @@ -297,8 +296,7 @@ describe("Renderer E2E", () => { assert.equal( "(ProxyViewContainer " + "(StackLayout " + - "(#comment), " + // ng-reflect comment - "(Label[text=Else]), (#comment)))", // the content to be displayed and its anchor + "(Label[text=Else])))", // the content to be displayed dumpView(componentRoot, true)); }); @@ -313,9 +311,7 @@ describe("Renderer E2E", () => { assert.equal( "(ProxyViewContainer " + "(StackLayout " + - "(#comment), " + // ng-reflect comment - "(Label[text=Then]), (#comment), " + // the content to be displayed and its anchor - "(#comment)))", // the anchor for the else template + "(Label[text=Then])))", // the content to be displayed dumpView(componentRoot, true)); }); }); @@ -331,9 +327,7 @@ describe("Renderer E2E", () => { assert.equal( "(ProxyViewContainer " + "(StackLayout " + - "(#comment), " + // ng-reflect comment - "(Label[text=Else]), (#comment), " + // the content to be displayed and its anchor - "(#comment)))", // the anchor for the else template + "(Label[text=Else])))", // the content to be displayed dumpView(componentRoot, true)); }); @@ -343,7 +337,7 @@ describe("Renderer E2E", () => { return testApp.loadComponent(NgForLabel).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal( - "(ProxyViewContainer (#comment), (Label[text=one]), (Label[text=two]), (Label[text=three]))", + "(ProxyViewContainer (Label[text=one]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true)); }); }); @@ -357,7 +351,7 @@ describe("Renderer E2E", () => { testApp.appRef.tick(); assert.equal( - "(ProxyViewContainer (#comment), (Label[text=one]), (Label[text=three]))", + "(ProxyViewContainer (Label[text=one]), (Label[text=three]))", dumpView(componentRoot, true)); }); }); @@ -371,7 +365,7 @@ describe("Renderer E2E", () => { testApp.appRef.tick(); assert.equal( - "(ProxyViewContainer (#comment), " + + "(ProxyViewContainer " + "(Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true)); }); From 7a86de7efe9bd944943a0570526daada309fd074 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Fri, 5 May 2017 20:23:37 +0300 Subject: [PATCH 6/7] refactor(renderer): create DetachedElements instead for comments and text nodes --- nativescript-angular/directives/action-bar.ts | 4 +- .../directives/list-view-comp.ts | 58 ++++++++++--------- nativescript-angular/directives/tab-view.ts | 18 ++++-- nativescript-angular/element-registry.ts | 15 +++-- nativescript-angular/renderer.ts | 6 +- nativescript-angular/view-util.ts | 45 +++++--------- 6 files changed, 72 insertions(+), 74 deletions(-) diff --git a/nativescript-angular/directives/action-bar.ts b/nativescript-angular/directives/action-bar.ts index bb9076a4b..4f9215857 100644 --- a/nativescript-angular/directives/action-bar.ts +++ b/nativescript-angular/directives/action-bar.ts @@ -7,7 +7,7 @@ import { registerElement, ViewClassMeta, NgView } from "../element-registry"; const actionBarMeta: ViewClassMeta = { skipAddToDom: true, - insertChild: (parent: NgView, child: NgView, atIndex: number) => { + insertChild: (parent: NgView, child: NgView) => { const bar = (parent); const childView = child; @@ -17,8 +17,6 @@ const actionBarMeta: ViewClassMeta = { } else if (child instanceof ActionItem) { bar.actionItems.addItem(childView); childView.parent = bar; - } else if (child.nodeName === "#comment") { - bar._addView(childView, atIndex); } else if (child instanceof View) { bar.titleView = childView; } diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts index 781e80a1d..6bf6fac95 100644 --- a/nativescript-angular/directives/list-view-comp.ts +++ b/nativescript-angular/directives/list-view-comp.ts @@ -1,29 +1,31 @@ import { + AfterContentInit, + ChangeDetectionStrategy, + ChangeDetectorRef, Component, + ContentChild, Directive, - Input, DoCheck, - OnDestroy, - AfterContentInit, ElementRef, - ViewContainerRef, - TemplateRef, - ContentChild, EmbeddedViewRef, - IterableDiffers, - IterableDiffer, - ChangeDetectorRef, EventEmitter, - ViewChild, - Output, Host, - ChangeDetectionStrategy + Input, + IterableDiffer, + IterableDiffers, + OnDestroy, + Output, + TemplateRef, + ViewChild, + ViewContainerRef, } from "@angular/core"; -import { isListLikeIterable } from "../collection-facade"; import { ListView, ItemEventData } from "tns-core-modules/ui/list-view"; import { View, KeyedTemplate } from "tns-core-modules/ui/core/view"; import { ObservableArray } from "tns-core-modules/data/observable-array"; import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; + +import { DetachedElement } from "../element-registry"; +import { isListLikeIterable } from "../collection-facade"; import { listViewLog, listViewError } from "../trace"; const NG_VIEW = "_ngViewRef"; @@ -212,23 +214,27 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit { } function getSingleViewRecursive(nodes: Array, nestLevel: number): View { - const actualNodes = nodes.filter((n) => !!n && n.nodeName !== "#text"); + const actualNodes = nodes.filter(node => !(node instanceof DetachedElement)); if (actualNodes.length === 0) { - throw new Error("No suitable views found in list template! Nesting level: " + nestLevel); + throw new Error(`No suitable views found in list template! ` + + `Nesting level: ${nestLevel}`); } else if (actualNodes.length > 1) { - throw new Error("More than one view found in list template! Nesting level: " + nestLevel); - } else { - if (actualNodes[0]) { - let parentLayout = actualNodes[0].parent; - if (parentLayout instanceof LayoutBase) { - parentLayout.removeChild(actualNodes[0]); - } - return actualNodes[0]; - } else { - return getSingleViewRecursive(actualNodes[0].children, nestLevel + 1); - } + throw new Error(`More than one view found in list template!` + + `Nesting level: ${nestLevel}`); + } + + const rootLayout = actualNodes[0]; + if (!rootLayout) { + return getSingleViewRecursive(rootLayout.children, nestLevel + 1); + } + + let parentLayout = rootLayout.parent; + if (parentLayout instanceof LayoutBase) { + parentLayout.removeChild(rootLayout); } + + return rootLayout; } export interface ComponentView { diff --git a/nativescript-angular/directives/tab-view.ts b/nativescript-angular/directives/tab-view.ts index 860b7adf6..79a2636d7 100644 --- a/nativescript-angular/directives/tab-view.ts +++ b/nativescript-angular/directives/tab-view.ts @@ -1,5 +1,15 @@ -import { ElementRef, Directive, Input, TemplateRef, ViewContainerRef, OnInit, AfterViewInit } from "@angular/core"; +import { + AfterViewInit, + Directive, + ElementRef, + Input, + OnInit, + TemplateRef, + ViewContainerRef, +} from "@angular/core"; import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view"; + +import { DetachedElement } from "../element-registry"; import { convertToInt } from "../common/utils"; import { rendererLog } from "../trace"; import { isBlank } from "../lang-facade"; @@ -94,9 +104,9 @@ export class TabViewItemDirective implements OnInit { } const viewRef = this.viewContainer.createEmbeddedView(this.templateRef); - // Filter out text nodes, etc - const realViews = viewRef.rootNodes.filter((node) => - node.nodeName && node.nodeName !== "#text"); + // Filter out text nodes and comments + const realViews = viewRef.rootNodes.filter(node => + !(node instanceof DetachedElement)); if (realViews.length > 0) { this.item.view = realViews[0]; diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts index 755005f95..637302995 100644 --- a/nativescript-angular/element-registry.ts +++ b/nativescript-angular/element-registry.ts @@ -1,7 +1,8 @@ import { View } from "tns-core-modules/ui/core/view"; export type ViewResolver = () => ViewClass; -export type NgView = View & ViewExtensions; +export type NgView = (View & ViewExtensions); +export type NgElement = NgView | DetachedElement; export interface ViewClassMeta { skipAddToDom?: boolean; insertChild?: (parent: NgView, child: NgView, atIndex: number) => void; @@ -20,6 +21,11 @@ export interface ViewClass { new (): View; } +export class DetachedElement { + templateParent: NgView; + meta: { skipAddToDom: true }; +} + const defaultViewMeta: ViewClassMeta = { skipAddToDom: false, }; @@ -110,10 +116,3 @@ registerElement("Span", () => require("tns-core-modules/text/span").Span); registerElement("DetachedContainer", () => require("tns-core-modules/ui/proxy-view-container").ProxyViewContainer, { skipAddToDom: true }); - -registerElement("DetachedText", () => require("ui/placeholder").Placeholder, - { skipAddToDom: true }); - -registerElement("Comment", () => require("ui/placeholder").Placeholder, - { skipAddToDom: true }); - diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index ccc6be962..ea2565dae 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -12,7 +12,7 @@ import { topmost } from "tns-core-modules/ui/frame"; import { APP_ROOT_VIEW, DEVICE, getRootPage } from "./platform-providers"; import { isBlank } from "./lang-facade"; import { ViewUtil } from "./view-util"; -import { NgView } from "./element-registry"; +import { NgView, DetachedElement } from "./element-registry"; import { rendererLog as traceLog } from "./trace"; // CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application. @@ -111,7 +111,7 @@ export class NativeScriptRenderer extends Renderer2 { return this.viewUtil.nextSiblingIndex(node); } - createComment(_value: any) { + createComment(_value: any): DetachedElement { traceLog(`NativeScriptRenderer.createComment ${_value}`); return this.viewUtil.createComment(); } @@ -121,7 +121,7 @@ export class NativeScriptRenderer extends Renderer2 { return this.viewUtil.createView(name); } - createText(_value: string) { + createText(_value: string): DetachedElement { traceLog(`NativeScriptRenderer.createText ${_value}`); return this.viewUtil.createText(); } diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index c8b816ca0..a48a490a1 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -8,6 +8,8 @@ import { getViewMeta, isKnownView, ViewExtensions, + DetachedElement, + NgElement, NgView, } from "./element-registry"; import { platformNames, Device } from "tns-core-modules/platform"; @@ -47,13 +49,13 @@ export class ViewUtil { this.isAndroid = device.os === platformNames.android; } - public insertChild(parent: any, child: NgView, atIndex: number = -1) { - if (!parent) { + public insertChild(parent: any, child: NgElement, atIndex: number = -1) { + if (child instanceof DetachedElement) { + child.templateParent = parent; return; } - if (child.meta.skipAddToDom) { - child.templateParent = parent; + if (!parent || child.meta.skipAddToDom) { return; } @@ -61,7 +63,7 @@ export class ViewUtil { parent.meta.insertChild(parent, child, atIndex); } else if (isLayout(parent)) { if (child.parent === parent) { - let index = (parent).getChildIndex(child); + const index = (parent).getChildIndex(child); if (index !== -1) { parent.removeChild(child); } @@ -72,12 +74,7 @@ export class ViewUtil { parent.addChild(child); } } else if (isContentView(parent)) { - // Explicit handling of template anchors inside ContentView - if (child.nodeName === "#comment") { - parent._addView(child, atIndex); - } else { - parent.content = child; - } + parent.content = child; } else if (parent && parent._addChildFromBuilder) { parent._addChildFromBuilder(child.nodeName, child); } else { @@ -85,8 +82,8 @@ export class ViewUtil { } } - public removeChild(parent: any, child: NgView) { - if (!parent || child.meta.skipAddToDom) { + public removeChild(parent: any, child: NgElement) { + if (!parent || child instanceof DetachedElement || child.meta.skipAddToDom) { return; } @@ -98,11 +95,6 @@ export class ViewUtil { if (parent.content === child) { parent.content = null; } - - // Explicit handling of template anchors inside ContentView - if (child.nodeName === "#comment") { - parent._removeView(child); - } } else if (isView(parent)) { parent._removeView(child); } else { @@ -120,20 +112,12 @@ export class ViewUtil { } } - public createComment(): NgView { - const commentView = this.createView("Comment"); - commentView.nodeName = "#comment"; - commentView.visibility = "collapse"; - - return commentView; + public createComment(): DetachedElement { + return new DetachedElement(); } - public createText(): NgView { - const detachedText = this.createView("DetachedText"); - detachedText.nodeName = "#text"; - detachedText.visibility = "collapse"; - - return detachedText; + public createText(): DetachedElement { + return new DetachedElement(); } public createView(name: string): NgView { @@ -142,6 +126,7 @@ export class ViewUtil { if (!isKnownView(name)) { name = "ProxyViewContainer"; } + const viewClass = getViewClass(name); let view = new viewClass(); view.nodeName = name; From 108e4d04e327590308902040525e767f4a1ec5ec Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Mon, 8 May 2017 10:21:44 +0300 Subject: [PATCH 7/7] refactor: move NgView, NgElement and similar to separate module --- .../animations/animation-driver.ts | 2 +- .../animations/animation-engine.ts | 2 +- .../animations/animation-player.ts | 2 +- nativescript-angular/animations/dom-utils.ts | 2 +- nativescript-angular/directives/action-bar.ts | 6 +- .../directives/list-view-comp.ts | 4 +- nativescript-angular/directives/tab-view.ts | 4 +- nativescript-angular/element-registry.ts | 38 ++----------- nativescript-angular/element-types.ts | 32 +++++++++++ nativescript-angular/index.ts | 11 +++- nativescript-angular/renderer.ts | 6 +- nativescript-angular/view-util.ts | 25 ++++++--- ng-sample/app/app.ts | 4 +- ng-sample/app/examples/renderer-test.html | 56 ++++++++++--------- tests/app/tests/property-sets.ts | 2 +- tests/app/tests/renderer-tests.ts | 2 +- 16 files changed, 108 insertions(+), 90 deletions(-) create mode 100644 nativescript-angular/element-types.ts diff --git a/nativescript-angular/animations/animation-driver.ts b/nativescript-angular/animations/animation-driver.ts index d1dd2b032..a2e6de0ca 100644 --- a/nativescript-angular/animations/animation-driver.ts +++ b/nativescript-angular/animations/animation-driver.ts @@ -1,6 +1,6 @@ import { AnimationPlayer } from "@angular/animations"; -import { NgView } from "../element-registry"; +import { NgView } from "../element-types"; import { NativeScriptAnimationPlayer } from "./animation-player"; import { Keyframe } from "./utils"; diff --git a/nativescript-angular/animations/animation-engine.ts b/nativescript-angular/animations/animation-engine.ts index 0dcbd51ff..527ae603c 100644 --- a/nativescript-angular/animations/animation-engine.ts +++ b/nativescript-angular/animations/animation-engine.ts @@ -1,7 +1,7 @@ import { ɵDomAnimationEngine as DomAnimationEngine } from "@angular/animations/browser"; import { AnimationEvent, AnimationPlayer } from "@angular/animations"; -import { NgView } from "../element-registry"; +import { NgView } from "../element-types"; import { copyArray, cssClasses, diff --git a/nativescript-angular/animations/animation-player.ts b/nativescript-angular/animations/animation-player.ts index fcc60d8f1..d4f648c15 100644 --- a/nativescript-angular/animations/animation-player.ts +++ b/nativescript-angular/animations/animation-player.ts @@ -4,7 +4,7 @@ import { KeyframeAnimationInfo, } from "tns-core-modules/ui/animation/keyframe-animation"; -import { NgView } from "../element-registry"; +import { NgView } from "../element-types"; import { Keyframe, getAnimationCurve, parseAnimationKeyframe } from "./utils"; export class NativeScriptAnimationPlayer implements AnimationPlayer { diff --git a/nativescript-angular/animations/dom-utils.ts b/nativescript-angular/animations/dom-utils.ts index 00584f70e..c6a54039b 100644 --- a/nativescript-angular/animations/dom-utils.ts +++ b/nativescript-angular/animations/dom-utils.ts @@ -7,7 +7,7 @@ import { } from "@angular/animations"; import { unsetValue } from "tns-core-modules/ui/core/view"; -import { NgView } from "../element-registry"; +import { NgView } from "../element-types"; // overriden to use the default 'unsetValue' // instead of empty string '' diff --git a/nativescript-angular/directives/action-bar.ts b/nativescript-angular/directives/action-bar.ts index 4f9215857..876befa45 100644 --- a/nativescript-angular/directives/action-bar.ts +++ b/nativescript-angular/directives/action-bar.ts @@ -1,9 +1,11 @@ import { Directive, Component, ElementRef, Optional, OnDestroy } from "@angular/core"; import { ActionItem, ActionBar, NavigationButton } from "tns-core-modules/ui/action-bar"; -import { isBlank } from "../lang-facade"; import { Page } from "tns-core-modules/ui/page"; import { View } from "tns-core-modules/ui/core/view"; -import { registerElement, ViewClassMeta, NgView } from "../element-registry"; + +import { isBlank } from "../lang-facade"; +import { registerElement } from "../element-registry"; +import { ViewClassMeta, NgView } from "../element-types"; const actionBarMeta: ViewClassMeta = { skipAddToDom: true, diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts index 6bf6fac95..ccfcac8f0 100644 --- a/nativescript-angular/directives/list-view-comp.ts +++ b/nativescript-angular/directives/list-view-comp.ts @@ -24,7 +24,7 @@ import { View, KeyedTemplate } from "tns-core-modules/ui/core/view"; import { ObservableArray } from "tns-core-modules/data/observable-array"; import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; -import { DetachedElement } from "../element-registry"; +import { CommentNode } from "../element-types"; import { isListLikeIterable } from "../collection-facade"; import { listViewLog, listViewError } from "../trace"; @@ -214,7 +214,7 @@ export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit { } function getSingleViewRecursive(nodes: Array, nestLevel: number): View { - const actualNodes = nodes.filter(node => !(node instanceof DetachedElement)); + const actualNodes = nodes.filter(node => !(node instanceof CommentNode)); if (actualNodes.length === 0) { throw new Error(`No suitable views found in list template! ` + diff --git a/nativescript-angular/directives/tab-view.ts b/nativescript-angular/directives/tab-view.ts index 79a2636d7..984dc0076 100644 --- a/nativescript-angular/directives/tab-view.ts +++ b/nativescript-angular/directives/tab-view.ts @@ -9,7 +9,7 @@ import { } from "@angular/core"; import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view"; -import { DetachedElement } from "../element-registry"; +import { CommentNode } from "../element-types"; import { convertToInt } from "../common/utils"; import { rendererLog } from "../trace"; import { isBlank } from "../lang-facade"; @@ -106,7 +106,7 @@ export class TabViewItemDirective implements OnInit { const viewRef = this.viewContainer.createEmbeddedView(this.templateRef); // Filter out text nodes and comments const realViews = viewRef.rootNodes.filter(node => - !(node instanceof DetachedElement)); + !(node instanceof CommentNode)); if (realViews.length > 0) { this.item.view = realViews[0]; diff --git a/nativescript-angular/element-registry.ts b/nativescript-angular/element-registry.ts index 637302995..fd889700f 100644 --- a/nativescript-angular/element-registry.ts +++ b/nativescript-angular/element-registry.ts @@ -1,37 +1,10 @@ -import { View } from "tns-core-modules/ui/core/view"; +import { ViewClass, ViewClassMeta } from "./element-types"; export type ViewResolver = () => ViewClass; -export type NgView = (View & ViewExtensions); -export type NgElement = NgView | DetachedElement; -export interface ViewClassMeta { - skipAddToDom?: boolean; - insertChild?: (parent: NgView, child: NgView, atIndex: number) => void; - removeChild?: (parent: NgView, child: NgView) => void; -} - -export interface ViewExtensions { - nodeType: number; - nodeName: string; - templateParent: NgView; - ngCssClasses: Map; - meta: ViewClassMeta; -} - -export interface ViewClass { - new (): View; -} - -export class DetachedElement { - templateParent: NgView; - meta: { skipAddToDom: true }; -} - -const defaultViewMeta: ViewClassMeta = { - skipAddToDom: false, -}; const elementMap = new Map(); const camelCaseSplit = /([a-z0-9])([A-Z])/g; +const defaultViewMeta: ViewClassMeta = { skipAddToDom: false }; export function registerElement( elementName: string, @@ -54,6 +27,7 @@ export function getViewClass(elementName: string): ViewClass { if (!entry) { throw new TypeError(`No known component for element ${elementName}.`); } + try { return entry.resolver(); } catch (e) { @@ -62,12 +36,8 @@ export function getViewClass(elementName: string): ViewClass { } export function getViewMeta(nodeName: string): ViewClassMeta { - let meta = defaultViewMeta; const entry = elementMap.get(nodeName) || elementMap.get(nodeName.toLowerCase()); - if (entry && entry.meta) { - meta = entry.meta; - } - return meta; + return (entry && entry.meta) || defaultViewMeta; } export function isKnownView(elementName: string): boolean { diff --git a/nativescript-angular/element-types.ts b/nativescript-angular/element-types.ts new file mode 100644 index 000000000..e04833bfc --- /dev/null +++ b/nativescript-angular/element-types.ts @@ -0,0 +1,32 @@ +import { View } from "tns-core-modules/ui/core/view"; + +export type NgView = (View & ViewExtensions); +export type NgElement = NgView | CommentNode; + +export interface ViewExtensions { + nodeType: number; + nodeName: string; + templateParent: NgView; + ngCssClasses: Map; + meta: ViewClassMeta; +} + +export interface ViewClass { + new (): View; +} + +// used for creating comments and text nodes in the renderer +export class CommentNode { + meta: { skipAddToDom: true }; + templateParent: NgView; +} + +export interface ViewClassMeta { + skipAddToDom?: boolean; + insertChild?: (parent: NgView, child: NgView, atIndex: number) => void; + removeChild?: (parent: NgView, child: NgView) => void; +} + +export function isDetachedElement(element): boolean { + return (element && element.meta && element.meta.skipAddToDom); +} diff --git a/nativescript-angular/index.ts b/nativescript-angular/index.ts index e1309f312..8ec8eaa23 100644 --- a/nativescript-angular/index.ts +++ b/nativescript-angular/index.ts @@ -1,4 +1,4 @@ -import "application"; +import "tns-core-modules/application"; export * from "./platform-common"; export * from "./platform"; @@ -15,13 +15,18 @@ export * from "./modal-dialog"; export * from "./renderer"; export * from "./view-util"; export * from "./resource-loader"; + export { ViewResolver, - ViewClass, - ViewClassMeta, registerElement, getViewClass, getViewMeta, isKnownView, } from "./element-registry"; + +export { + ViewClass, + ViewClassMeta, +} from "./element-types"; + export * from "./value-accessors/base-value-accessor"; diff --git a/nativescript-angular/renderer.ts b/nativescript-angular/renderer.ts index ea2565dae..1a9f0bfbc 100644 --- a/nativescript-angular/renderer.ts +++ b/nativescript-angular/renderer.ts @@ -12,7 +12,7 @@ import { topmost } from "tns-core-modules/ui/frame"; import { APP_ROOT_VIEW, DEVICE, getRootPage } from "./platform-providers"; import { isBlank } from "./lang-facade"; import { ViewUtil } from "./view-util"; -import { NgView, DetachedElement } from "./element-registry"; +import { NgView, CommentNode } from "./element-types"; import { rendererLog as traceLog } from "./trace"; // CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application. @@ -111,7 +111,7 @@ export class NativeScriptRenderer extends Renderer2 { return this.viewUtil.nextSiblingIndex(node); } - createComment(_value: any): DetachedElement { + createComment(_value: any): CommentNode { traceLog(`NativeScriptRenderer.createComment ${_value}`); return this.viewUtil.createComment(); } @@ -121,7 +121,7 @@ export class NativeScriptRenderer extends Renderer2 { return this.viewUtil.createView(name); } - createText(_value: string): DetachedElement { + createText(_value: string): CommentNode { traceLog(`NativeScriptRenderer.createText ${_value}`); return this.viewUtil.createText(); } diff --git a/nativescript-angular/view-util.ts b/nativescript-angular/view-util.ts index a48a490a1..f0b9cc73a 100644 --- a/nativescript-angular/view-util.ts +++ b/nativescript-angular/view-util.ts @@ -7,11 +7,15 @@ import { getViewClass, getViewMeta, isKnownView, +} from "./element-registry"; + +import { + CommentNode, ViewExtensions, - DetachedElement, NgElement, NgView, -} from "./element-registry"; + isDetachedElement, +} from "./element-types"; import { platformNames, Device } from "tns-core-modules/platform"; import { rendererLog as traceLog } from "./trace"; @@ -50,12 +54,12 @@ export class ViewUtil { } public insertChild(parent: any, child: NgElement, atIndex: number = -1) { - if (child instanceof DetachedElement) { + if (child instanceof CommentNode) { child.templateParent = parent; return; } - if (!parent || child.meta.skipAddToDom) { + if (!parent || isDetachedElement(child)) { return; } @@ -83,7 +87,10 @@ export class ViewUtil { } public removeChild(parent: any, child: NgElement) { - if (!parent || child instanceof DetachedElement || child.meta.skipAddToDom) { + if (!parent || + child instanceof CommentNode || + isDetachedElement(child)) { + return; } @@ -112,12 +119,12 @@ export class ViewUtil { } } - public createComment(): DetachedElement { - return new DetachedElement(); + public createComment(): CommentNode { + return new CommentNode(); } - public createText(): DetachedElement { - return new DetachedElement(); + public createText(): CommentNode { + return new CommentNode(); } public createView(name: string): NgView { diff --git a/ng-sample/app/app.ts b/ng-sample/app/app.ts index 3f803858a..27679de61 100644 --- a/ng-sample/app/app.ts +++ b/ng-sample/app/app.ts @@ -128,13 +128,13 @@ const customPageFactoryProvider = { // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletAppComponent)); // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletNestedAppComponent)); // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ClearHistoryAppComponent)); -// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(LoginAppComponent)); +//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(LoginAppComponent)); // animations // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationStatesTest)); // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationNgClassTest)); // platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationKeyframesTest)); -platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationEnterLeaveTest)); +// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationEnterLeaveTest)); // Livesync test let cachedUrl: string; diff --git a/ng-sample/app/examples/renderer-test.html b/ng-sample/app/examples/renderer-test.html index 674a1b4b3..b0c17d6f8 100644 --- a/ng-sample/app/examples/renderer-test.html +++ b/ng-sample/app/examples/renderer-test.html @@ -1,28 +1,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + diff --git a/tests/app/tests/property-sets.ts b/tests/app/tests/property-sets.ts index 88472109e..3cb2ca29d 100644 --- a/tests/app/tests/property-sets.ts +++ b/tests/app/tests/property-sets.ts @@ -2,7 +2,7 @@ import {assert} from "./test-config"; import {View} from "ui/core/view"; import {ViewUtil} from "nativescript-angular/view-util"; -import {NgView, ViewExtensions, ViewClassMeta} from "nativescript-angular/element-registry"; +import {NgView, ViewExtensions, ViewClassMeta} from "nativescript-angular/element-types"; import {Red} from "color/known-colors"; import {device, platformNames} from "platform"; import {createDevice} from "./test-utils"; diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index fecbaf982..bce870fd1 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -9,7 +9,7 @@ import { LayoutBase } from "ui/layouts/layout-base"; import { StackLayout } from "ui/layouts/stack-layout"; import { ContentView } from "ui/content-view"; import { Button } from "ui/button"; -import { NgView } from "nativescript-angular/element-registry"; +import { NgView } from "nativescript-angular/element-types"; @Component({ template: ``