Skip to content

Commit 81bae5f

Browse files
author
Alexander Vakrilov
authored
Merge pull request #560 from NativeScript/blank-page-fix
FIX: Adding template anchors to ContentView
2 parents 80d9af8 + bd4aa70 commit 81bae5f

File tree

6 files changed

+158
-59
lines changed

6 files changed

+158
-59
lines changed

nativescript-angular/directives/action-bar.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Directive, Component, ElementRef, Optional } from "@angular/core";
22
import { ActionItem, ActionBar, NavigationButton } from "ui/action-bar";
33
import { isBlank } from "../lang-facade";
4-
import {Page} from "ui/page";
4+
import { Page } from "ui/page";
55
import { View } from "ui/core/view";
6-
import { registerElement, ViewClassMeta, NgView } from "../element-registry";
6+
import { registerElement, ViewClassMeta, NgView, TEMPLATE } from "../element-registry";
77

88
let actionBarMeta: ViewClassMeta = {
99
skipAddToDom: true,
@@ -17,7 +17,7 @@ let actionBarMeta: ViewClassMeta = {
1717
} else if (child instanceof ActionItem) {
1818
bar.actionItems.addItem(childView);
1919
childView.parent = bar;
20-
} else if (child.nodeName === "template") {
20+
} else if (child.nodeName === TEMPLATE) {
2121
child.templateParent = parent;
2222
} else if (child.nodeName !== "#text" && child instanceof View) {
2323
bar.titleView = childView;
@@ -34,8 +34,8 @@ let actionBarMeta: ViewClassMeta = {
3434
} else if (child instanceof ActionItem) {
3535
bar.actionItems.removeItem(childView);
3636
childView.parent = null;
37-
} else if (child.nodeName !== "template" && child instanceof View &&
38-
bar.titleView && bar.titleView === childView) {
37+
} else if (child.nodeName !== TEMPLATE && child instanceof View &&
38+
bar.titleView && bar.titleView === childView) {
3939
bar.titleView = null;
4040
}
4141
},

nativescript-angular/element-registry.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {View} from "ui/core/view";
1+
import { View } from "ui/core/view";
22

33
export type ViewResolver = () => ViewClass;
44
export type NgView = View & ViewExtensions;
5+
export const TEMPLATE = "template";
56

67
export interface ViewClassMeta {
78
skipAddToDom?: boolean;
89
insertChild?: (parent: NgView, child: NgView, atIndex: number) => void;
910
removeChild?: (parent: NgView, child: NgView) => void;
11+
isTemplateAnchor?: boolean;
1012
}
1113

1214
export interface ViewExtensions {
@@ -24,7 +26,7 @@ const defaultViewMeta: ViewClassMeta = {
2426
skipAddToDom: false,
2527
};
2628

27-
const elementMap = new Map<string, { resolver: ViewResolver, meta?: ViewClassMeta }>();
29+
const elementMap = new Map<string, { resolver: ViewResolver, meta?: ViewClassMeta }>();
2830
const camelCaseSplit = /([a-z0-9])([A-Z])/g;
2931

3032
export function registerElement(
@@ -69,6 +71,10 @@ export function isKnownView(elementName: string): boolean {
6971
elementMap.has(elementName.toLowerCase());
7072
}
7173

74+
// Empty view used for template anchors
75+
export class TemplateView extends View {
76+
}
77+
registerElement(TEMPLATE, () => TemplateView, { isTemplateAnchor: true });
7278

7379
// Register default NativeScript components
7480
// Note: ActionBar related components are registerd together with action-bar directives.

nativescript-angular/view-util.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
import {isString, isDefined} from "utils/types";
2-
import {View} from "ui/core/view";
3-
import {Placeholder} from "ui/placeholder";
4-
import {ContentView} from "ui/content-view";
5-
import {LayoutBase} from "ui/layouts/layout-base";
1+
import { isString, isDefined } from "utils/types";
2+
import { View } from "ui/core/view";
3+
import { Placeholder } from "ui/placeholder";
4+
import { ContentView } from "ui/content-view";
5+
import { LayoutBase } from "ui/layouts/layout-base";
66
import {
77
ViewClass,
88
getViewClass,
99
getViewMeta,
1010
isKnownView,
1111
ViewExtensions,
12-
NgView
12+
NgView,
13+
TEMPLATE
1314
} from "./element-registry";
14-
import {getSpecialPropertySetter} from "ui/builder/special-properties";
15-
import {StyleProperty, getPropertyByName, withStyleProperty} from "ui/styling/style-property";
16-
import {ValueSource} from "ui/core/dependency-observable";
17-
import {platformNames, Device} from "platform";
18-
import {rendererLog as traceLog, styleError} from "./trace";
15+
import { getSpecialPropertySetter } from "ui/builder/special-properties";
16+
import { StyleProperty, getPropertyByName, withStyleProperty } from "ui/styling/style-property";
17+
import { ValueSource } from "ui/core/dependency-observable";
18+
import { platformNames, Device } from "platform";
19+
import { rendererLog as traceLog, styleError } from "./trace";
1920

2021
const IOS_PREFX: string = ":ios:";
2122
const ANDROID_PREFX: string = ":android:";
@@ -70,7 +71,12 @@ export class ViewUtil {
7071
parent.addChild(child);
7172
}
7273
} else if (isContentView(parent)) {
73-
parent.content = child;
74+
// Explicit handling of template anchors inside ContentView
75+
if (child.meta.isTemplateAnchor) {
76+
parent._addView(child, atIndex);
77+
} else {
78+
parent.content = child;
79+
}
7480
} else if (parent && parent._addChildFromBuilder) {
7581
parent._addChildFromBuilder(child.nodeName, child);
7682
} else {
@@ -91,6 +97,11 @@ export class ViewUtil {
9197
if (parent.content === child) {
9298
parent.content = null;
9399
}
100+
101+
// Explicit handling of template anchors inside ContentView
102+
if (child.meta.isTemplateAnchor) {
103+
parent._removeView(child);
104+
}
94105
} else if (isView(parent)) {
95106
parent._removeView(child);
96107
} else {
@@ -152,10 +163,10 @@ export class ViewUtil {
152163
}
153164

154165
public createTemplateAnchor(parentElement: NgView) {
155-
// HACK: Using a ContentView here, so that it creates a native View object
156-
const anchor = this.createAndAttach("template", ContentView, parentElement);
157-
anchor.visibility = "collapse";
166+
const viewClass = getViewClass(TEMPLATE);
167+
const anchor = this.createAndAttach(TEMPLATE, viewClass, parentElement);
158168
anchor.templateParent = parentElement;
169+
traceLog("Created templateAnchor: " + anchor);
159170
return anchor;
160171
}
161172

ng-sample/app/app.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import { Page } from "ui/page";
1919
import { Color } from "color";
2020

2121
import trace = require("trace");
22-
// trace.setCategories(rendererTraceCategory);
22+
trace.setCategories(rendererTraceCategory);
2323
// trace.setCategories(routerTraceCategory);
24-
trace.setCategories(listViewTraceCategory);
24+
// trace.setCategories(listViewTraceCategory);
2525
trace.enable();
2626

2727
import { RendererTest } from './examples/renderer-test';
@@ -114,28 +114,29 @@ const customPageFactoryProvider = {
114114

115115
platformNativeScriptDynamic().bootstrapModule(makeExampleModule(RendererTest));
116116
// platformNativeScriptDynamic(undefined, [customPageFactoryProvider]).bootstrapModule(makeExampleModule(RendererTest));
117-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(TabViewTest));
118-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(Benchmark));
119-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ListTest));
117+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(TabViewTest));
118+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(Benchmark));
119+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ListTest));
120120
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ListTemplateSelectorTest));
121-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ListTestAsync));
122-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ImageTest));
121+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ListTestAsync));
122+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ImageTest));
123123
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ModalTest));
124-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(HttpTest));
125-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PlatfromDirectivesTest));
126-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ActionBarTest));
124+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(HttpTest));
125+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PlatfromDirectivesTest));
126+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ActionBarTest));
127127

128-
//new router
129-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(RouterOutletAppComponent));
128+
// router
129+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(RouterOutletAppComponent));
130130
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletAppComponent));
131-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletNestedAppComponent));
132-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ClearHistoryAppComponent));
133-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(LoginAppComponent));
134-
//animations
135-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationStatesTest));
136-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationNgClassTest));
137-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationKeyframesTest));
138-
//platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationEnterLeaveTest));
131+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(PageRouterOutletNestedAppComponent));
132+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(ClearHistoryAppComponent));
133+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(LoginAppComponent));
134+
135+
// animations
136+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationStatesTest));
137+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationNgClassTest));
138+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationKeyframesTest));
139+
// platformNativeScriptDynamic().bootstrapModule(makeExampleModule(AnimationEnterLeaveTest));
139140

140141
//Livesync test
141142
var cachedUrl: string;

ng-sample/hooks/before-livesync/nativescript-angular-sync .js

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/app/tests/renderer-tests.ts

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
//make sure you import mocha-config before @angular/core
2-
import {assert} from "./test-config";
3-
import {Component, ElementRef, Renderer, NgZone} from "@angular/core";
4-
import {ProxyViewContainer} from "ui/proxy-view-container";
5-
import {Red} from "color/known-colors";
6-
import {dumpView} from "./test-utils";
7-
import {TestApp} from "./test-app";
8-
import {LayoutBase} from "ui/layouts/layout-base";
9-
import {StackLayout} from "ui/layouts/stack-layout";
2+
import { assert } from "./test-config";
3+
import { Component, ElementRef, Renderer, NgZone } from "@angular/core";
4+
import { ProxyViewContainer } from "ui/proxy-view-container";
5+
import { Red } from "color/known-colors";
6+
import { dumpView } from "./test-utils";
7+
import { TestApp } from "./test-app";
8+
import { LayoutBase } from "ui/layouts/layout-base";
9+
import { StackLayout } from "ui/layouts/stack-layout";
10+
import { ContentView } from "ui/content-view";
11+
import { Button } from "ui/button";
12+
import { NgView } from "nativescript-angular/element-registry";
1013

1114
@Component({
1215
template: `<StackLayout><Label text="Layout"></Label></StackLayout>`
@@ -170,9 +173,9 @@ describe('Renderer E2E', () => {
170173
it("executes events inside NgZone when listen is called inside NgZone", (done) => {
171174
const eventName = "someEvent";
172175
const view = new StackLayout();
173-
const evetArg = { eventName, object: view };
176+
const eventArg = { eventName, object: view };
174177
const callback = (arg) => {
175-
assert.equal(arg, evetArg);
178+
assert.equal(arg, eventArg);
176179
assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone");
177180
done();
178181
};
@@ -183,25 +186,25 @@ describe('Renderer E2E', () => {
183186

184187
setTimeout(() => {
185188
testApp.zone.runOutsideAngular(() => {
186-
view.notify(evetArg);
189+
view.notify(eventArg);
187190
});
188191
}, 10);
189192
});
190193

191194
it("executes events inside NgZone when listen is called outside NgZone", (done) => {
192195
const eventName = "someEvent";
193196
const view = new StackLayout();
194-
const evetArg = { eventName, object: view };
197+
const eventArg = { eventName, object: view };
195198
const callback = (arg) => {
196-
assert.equal(arg, evetArg);
199+
assert.equal(arg, eventArg);
197200
assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone");
198201
done();
199202
};
200203

201204
testApp.zone.runOutsideAngular(() => {
202205
testApp.renderer.listen(view, eventName, callback);
203206

204-
view.notify(evetArg);
207+
view.notify(eventArg);
205208
});
206209
});
207210

@@ -265,7 +268,7 @@ describe('Renderer createElement', () => {
265268
return TestApp.create().then((app) => {
266269
testApp = app;
267270
renderer = testApp.renderer;
268-
})
271+
});
269272
});
270273

271274
after(() => {
@@ -291,4 +294,83 @@ describe('Renderer createElement', () => {
291294
const result = renderer.createElement(null, "unknown-tag", null);
292295
assert.instanceOf(result, ProxyViewContainer, "Renderer should create ProxyViewContainer form 'unknown-tag'")
293296
});
294-
})
297+
});
298+
299+
300+
describe('Renderer attach/detach', () => {
301+
let testApp: TestApp = null;
302+
let renderer: Renderer = null;
303+
304+
before(() => {
305+
return TestApp.create().then((app) => {
306+
testApp = app;
307+
renderer = testApp.renderer;
308+
});
309+
});
310+
311+
after(() => {
312+
testApp.dispose();
313+
});
314+
315+
it("createElement element with parent attaches element to content view", () => {
316+
const parent = <ContentView>renderer.createElement(null, "ContentView", null);
317+
const button = <Button>renderer.createElement(parent, "Button", null);
318+
319+
assert.equal(parent.content, button);
320+
assert.equal(button.parent, parent);
321+
});
322+
323+
it("createElement element with parent attaches element to layout view", () => {
324+
const parent = <StackLayout>renderer.createElement(null, "StackLayout", null);
325+
const button = <Button>renderer.createElement(parent, "Button", null);
326+
327+
assert.equal(parent.getChildAt(0), button);
328+
assert.equal(button.parent, parent);
329+
});
330+
331+
it("detachView element removes element from content view", () => {
332+
const parent = <ContentView>renderer.createElement(null, "ContentView", null);
333+
const button = <Button>renderer.createElement(parent, "Button", null);
334+
335+
renderer.detachView([button]);
336+
337+
assert.isNull(parent.content);
338+
assert.isUndefined(button.parent);
339+
});
340+
341+
it("detachView element removes element from layout view", () => {
342+
const parent = <StackLayout>renderer.createElement(null, "StackLayout", null);
343+
const button = <Button>renderer.createElement(parent, "Button", null);
344+
345+
renderer.detachView([button]);
346+
347+
assert.equal(parent.getChildrenCount(), 0);
348+
assert.isUndefined(button.parent);
349+
});
350+
351+
it("attaching template anchor in content view does not replace its content", () => {
352+
const parent = <ContentView>renderer.createElement(null, "ContentView", null);
353+
const button = <Button>renderer.createElement(parent, "Button", null);
354+
355+
assert.equal(parent.content, button);
356+
357+
const anchor = <NgView>renderer.createTemplateAnchor(parent);
358+
359+
assert.equal(parent.content, button);
360+
assert.equal(anchor.parent, parent);
361+
assert.equal(anchor.templateParent, parent);
362+
});
363+
364+
it("attaching and detaching template anchor to content view does not affect its content", () => {
365+
const parent = <ContentView>renderer.createElement(null, "ContentView", null);
366+
const button = <Button>renderer.createElement(parent, "Button", null);
367+
const anchor = <NgView>renderer.createTemplateAnchor(null);
368+
assert.equal(parent.content, button);
369+
370+
renderer.attachViewAfter(button, [anchor]);
371+
assert.equal(parent.content, button);
372+
373+
renderer.detachView([anchor]);
374+
assert.equal(parent.content, button);
375+
});
376+
});

0 commit comments

Comments
 (0)