diff --git a/.ctags-exclude b/.ctags-exclude
index cd8ec8e08..6e7c9dcf0 100644
--- a/.ctags-exclude
+++ b/.ctags-exclude
@@ -26,3 +26,4 @@ src/angular2
web/*.js
src/*.js
deps/angular/dist
+deps/angular/tmp
diff --git a/ng-sample/app/app.css b/ng-sample/app/app.css
index 17e1cb37f..bf8e9bd0e 100644
--- a/ng-sample/app/app.css
+++ b/ng-sample/app/app.css
@@ -14,6 +14,6 @@
}
button {
- font-size: 42;
+ font-size: 20;
horizontal-align: center;
}
diff --git a/ng-sample/app/renderer-test.ts b/ng-sample/app/renderer-test.ts
index b02729d32..318b49e03 100644
--- a/ng-sample/app/renderer-test.ts
+++ b/ng-sample/app/renderer-test.ts
@@ -1,42 +1,45 @@
-import {Inject, Component, View} from 'angular2/core';
+import {Component, Directive, Host, ElementRef, Input} from 'angular2/core';
@Component({
selector: 'templated-component',
+ directives: [TemplatedComponent],
templateUrl: 'title.html'
})
export class TemplatedComponent {
+ @Input() public renderChild: boolean = false;
+ @Input() public text: string = "Hello, external templates";
+}
+
+@Directive({
+ selector: 'Progress',
+})
+export class ProgressComponent {
+ constructor(private element: ElementRef) {
+ }
+
+ ngOnInit() {
+ this.element.nativeElement.value = 90;
+ }
}
@Component({
selector: 'renderer-test',
- directives: [TemplatedComponent],
+ directives: [TemplatedComponent, ProgressComponent],
template: `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
`,
})
export class RendererTest {
@@ -54,9 +57,8 @@ export class RendererTest {
this.moreDetailsText = 'More details:';
this.detailLines = [
- "ngFor inside a ngIf",
- "Street address",
- "Country, city",
+ "ngFor inside a ngIf 1",
+ "ngFor inside a ngIf 2",
];
}
diff --git a/ng-sample/app/title.html b/ng-sample/app/title.html
index 235ab44fa..c7537fac5 100644
--- a/ng-sample/app/title.html
+++ b/ng-sample/app/title.html
@@ -1 +1,2 @@
-
+
+
diff --git a/ng-sample/tsconfig.json b/ng-sample/tsconfig.json
index f3b8d24ad..66fb9f9eb 100644
--- a/ng-sample/tsconfig.json
+++ b/ng-sample/tsconfig.json
@@ -35,5 +35,9 @@
"filesGlob": [
"node_modules/tns-core-modules/tns-core-modules.d.ts",
"app/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules",
+ "platforms"
]
}
\ No newline at end of file
diff --git a/package.json b/package.json
index b357b5552..aca19ff69 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
},
"scripts": {},
"dependencies": {
- "angular2": "2.0.0-beta.0",
+ "angular2": "2.0.0-beta.1",
"parse5": "1.4.2",
"punycode": "1.3.2",
"querystring": "0.2.0",
diff --git a/src/nativescript-angular/application.ts b/src/nativescript-angular/application.ts
index 293a566d0..90427298d 100644
--- a/src/nativescript-angular/application.ts
+++ b/src/nativescript-angular/application.ts
@@ -13,8 +13,8 @@ import {platform, ComponentRef, PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angul
import {bind, provide, Provider} from 'angular2/src/core/di';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
-import {Renderer} from 'angular2/src/core/render/api';
-import {NativeScriptRenderer} from './renderer';
+import {RootRenderer, Renderer} from 'angular2/src/core/render/api';
+import {NativeScriptRootRenderer, NativeScriptRenderer} from './renderer';
import {NativeScriptDomAdapter} from './dom_adapter';
import {XHR} from 'angular2/src/compiler/xhr';
import {FileSystemXHR} from './xhr';
@@ -34,6 +34,8 @@ export function nativeScriptBootstrap(appComponentType: any,
NativeScriptDomAdapter.makeCurrent();
let nativeScriptProviders: ProviderArray = [
+ NativeScriptRootRenderer,
+ provide(RootRenderer, {useClass: NativeScriptRootRenderer}),
NativeScriptRenderer,
provide(Renderer, {useClass: NativeScriptRenderer}),
provide(XHR, {useClass: FileSystemXHR}),
diff --git a/src/nativescript-angular/renderer.ts b/src/nativescript-angular/renderer.ts
index 668e7d076..e7bdb1baf 100644
--- a/src/nativescript-angular/renderer.ts
+++ b/src/nativescript-angular/renderer.ts
@@ -1,261 +1,160 @@
import {Inject, Injectable} from 'angular2/src/core/di';
-import {RenderComponentTemplate} from 'angular2/src/core/render/api';
-import {createRenderView, NodeFactory} from 'angular2/src/core/render/view_factory';
import {
Renderer,
- RenderEventDispatcher,
- RenderElementRef,
- RenderProtoViewRef,
- RenderViewRef,
- RenderFragmentRef,
- RenderViewWithFragments,
- RenderTemplateCmd
+ RootRenderer,
+ RenderComponentType
} from 'angular2/src/core/render/api';
import {isBlank} from 'angular2/src/facade/lang';
-import {
- DefaultProtoViewRef,
- DefaultRenderView,
- DefaultRenderFragmentRef
-} from 'angular2/src/core/render/view';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
-import {ViewNode, DummyViewNode} from './view_node';
+import {View} from "ui/core/view";
+import {topmost} from 'ui/frame';
+import * as util from "./view-util";
//var console = {log: function(msg) {}}
@Injectable()
-export class NativeScriptRenderer extends Renderer implements NodeFactory {
- constructor() {
+export class NativeScriptRootRenderer extends RootRenderer {
+ private _registeredComponents: Map = new Map();
+
+ renderComponent(componentProto: RenderComponentType): Renderer {
+ var renderer = this._registeredComponents.get(componentProto.id);
+ if (isBlank(renderer)) {
+ renderer = new NativeScriptRenderer(this, componentProto);
+ this._registeredComponents.set(componentProto.id, renderer);
+ }
+ return renderer;
+ }
+}
+
+@Injectable()
+export class NativeScriptRenderer extends Renderer {
+ constructor(private _rootRenderer: NativeScriptRootRenderer, private componentProto: RenderComponentType) {
super();
console.log('NativeScriptRenderer created');
}
- public createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
- console.log('NativeScriptRenderer.createProtoView: ' + componentTemplateId + ' -> ' + cmds);
- return new DefaultProtoViewRef(this._componentTemplates.get(componentTemplateId), cmds);
+ renderComponent(componentProto: RenderComponentType): Renderer {
+ return this._rootRenderer.renderComponent(componentProto);
}
- public createRootHostView(
- hostProtoViewRef: RenderProtoViewRef,
- fragmentCount: number,
- hostElementSelector: string
- ): RenderViewWithFragments {
- console.log("NativeScriptRenderer.createRootHostView");
-
- let rootViewWithFragments = this._createView(hostProtoViewRef, null);
-
- let rootView = resolveInternalDomView(rootViewWithFragments.viewRef);
- let rootNode = rootView.boundElements[0];
- rootNode.attachToView();
-
- return rootViewWithFragments;
+ selectRootElement(selector: string): util.NgView {
+ console.log('ROOT');
+ const page = topmost().currentPage;
+ page.nodeName = 'Page';
+ return page;
}
- public createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments {
- console.log("NativeScriptRenderer.createView");
- return this._createView(protoViewRef, null);
+ createViewRoot(hostElement: util.NgView): util.NgView {
+ console.log('CREATE VIEW ROOT: ' + hostElement.nodeName);
+ return hostElement;
}
- private _createView(protoViewRef: RenderProtoViewRef, inplaceElement: HTMLElement): RenderViewWithFragments {
- var dpvr = protoViewRef;
- var view = createRenderView(dpvr.template, dpvr.cmds, inplaceElement, this);
- return new RenderViewWithFragments(view, view.fragments);
+ projectNodes(parentElement: util.NgView, nodes: util.NgView[]): void {
+ console.log('NativeScriptRenderer.projectNodes');
+ nodes.forEach((node) => {
+ util.insertChild(parentElement, node);
+ });
}
- public destroyView(viewRef: RenderViewRef) {
- console.log("NativeScriptRenderer.destroyView");
- // Seems to be called on component dispose only (router outlet)
- //TODO: handle this when we resolve routing and navigation.
- }
+ attachViewAfter(anchorNode: util.NgView, viewRootNodes: util.NgView[]) {
+ console.log('NativeScriptRenderer.attachViewAfter: ' + anchorNode.nodeName + ' ' + anchorNode);
+ const parent = anchorNode.parent;
+ const insertPosition = util.getChildIndex(parent, anchorNode);
- public getRootNodes(fragment: RenderFragmentRef): ViewNode[] {
- return resolveInternalDomFragment(fragment);
+ viewRootNodes.forEach((node, index) => {
+ const childIndex = insertPosition + index + 1;
+ util.insertChild(parent, node, childIndex);
+ this.animateNodeEnter(node);
+ });
}
- public attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef, fragmentRef: RenderFragmentRef) {
- console.log("NativeScriptRenderer.attachFragmentAfterFragment");
-
- var previousFragmentNodes = resolveInternalDomFragment(previousFragmentRef);
- if (previousFragmentNodes.length > 0) {
- var sibling = previousFragmentNodes[previousFragmentNodes.length - 1];
- let nodes = resolveInternalDomFragment(fragmentRef);
- this.attachFragmentAfter(sibling, nodes);
+ detachView(viewRootNodes: util.NgView[]) {
+ console.log('NativeScriptRenderer.detachView');
+ for (var i = 0; i < viewRootNodes.length; i++) {
+ var node = viewRootNodes[i];
+ util.removeChild(node.parent, node);
+ this.animateNodeLeave(node);
}
}
- public attachFragmentAfterElement(location: RenderElementRef, fragmentRef: RenderFragmentRef) {
- console.log("NativeScriptRenderer.attachFragmentAfterElement");
-
- let element = resolveBoundNode(location);
- let nodes = resolveInternalDomFragment(fragmentRef);
- this.attachFragmentAfter(element, nodes);
- }
-
- private attachFragmentAfter(anchorNode: ViewNode, fragmentNodes: ViewNode[]) {
- var startIndex = anchorNode.parentNode.getChildIndex(anchorNode) + 1;
-
- fragmentNodes.forEach((node, index) => {
- console.log('attachFragmentAfter: child: ' + node.viewName + ' after: ' + anchorNode.viewName + ' startIndex: ' + startIndex + ' index: ' + index);
- anchorNode.parentNode.insertChildAt(startIndex + index, node);
- node.attachToView(startIndex + index);
- });
- }
-
- detachFragment(fragmentRef: RenderFragmentRef) {
- console.log('NativeScriptRenderer.detachFragment');
-
- var fragmentNodes = resolveInternalDomFragment(fragmentRef);
- fragmentNodes.forEach((node) => {
- console.log('detaching fragment child: ' + node.viewName);
- if (node.parentNode)
- node.parentNode.removeChild(node);
- });
+ animateNodeEnter(node: util.NgView) {
}
- hydrateView(viewRef: RenderViewRef) {
- console.log("NativeScriptRenderer.hydrateView ");
- //DOING nothing -- the view init code happens on attach: ViewNode#createUI
+ animateNodeLeave(node: util.NgView) {
}
- dehydrateView(viewRef: RenderViewRef) {
- console.log("NativeScriptRenderer.dehydrateView");
- //TODO: detach events
+ public destroyView(hostElement: util.NgView, viewAllNodes: util.NgView[]) {
+ console.log("NativeScriptRenderer.destroyView");
+ // Seems to be called on component dispose only (router outlet)
+ //TODO: handle this when we resolve routing and navigation.
}
- setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any) {
- console.log("NativeScriptRenderer.setElementProperty " + propertyName + " = " + propertyValue);
-
- let node = resolveBoundNode(location);
- node.setProperty(propertyName, propertyValue);
+ setElementProperty(renderElement: util.NgView, propertyName: string, propertyValue: any) {
+ console.log("NativeScriptRenderer.setElementProperty " + renderElement.nodeName + ': ' + propertyName + " = " + propertyValue);
+ util.setProperty(renderElement, propertyName, propertyValue);
}
- setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string) {
- console.log("NativeScriptRenderer.setElementAttribute " + attributeName + " = " + attributeValue);
- return this.setElementProperty(location, attributeName, attributeValue);
+ setElementAttribute(renderElement: util.NgView, attributeName: string, attributeValue: string) {
+ console.log("NativeScriptRenderer.setElementAttribute " + renderElement.nodeName + ': ' + attributeName + " = " + attributeValue);
+ return this.setElementProperty(renderElement, attributeName, attributeValue);
}
- setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void {
+ setElementClass(renderElement: util.NgView, className: string, isAdd: boolean): void {
console.log("NativeScriptRenderer.setElementClass " + className + " - " + isAdd);
- let node = resolveBoundNode(location);
if (isAdd) {
- node.addClass(className);
+ util.addClass(renderElement, className);
} else {
- node.removeClass(className);
+ util.removeClass(renderElement, className);
}
}
- setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void {
- let node = resolveBoundNode(location);
- node.setStyleProperty(styleName, styleValue);
+ setElementStyle(renderElement: util.NgView, styleName: string, styleValue: string): void {
+ util.setStyleProperty(renderElement, styleName, styleValue);
}
/**
* Used only in debug mode to serialize property changes to comment nodes,
* such as placeholders.
*/
- setBindingDebugInfo(location: RenderElementRef, propertyName: string, propertyValue: string): void {
- let node = resolveBoundNode(location);
- console.log('NativeScriptRenderer.setBindingDebugInfo: ' + node.viewName + ', ' + propertyName + ' = ' + propertyValue);
- }
-
-
- getNativeElementSync(location: RenderElementRef): any {
- console.log("NativeScriptRenderer.getNativeElementSync");
-
- let node = resolveBoundNode(location);
- return node.nativeView;
- }
-
- getViewNode(location: RenderElementRef): ViewNode {
- console.log("NativeScriptRenderer.getViewNode");
- return resolveBoundNode(location);
+ setBindingDebugInfo(renderElement: util.NgView, propertyName: string, propertyValue: string): void {
+ console.log('NativeScriptRenderer.setBindingDebugInfo: ' + renderElement.nodeName + ', ' + propertyName + ' = ' + propertyValue);
}
/**
* Calls a method on an element.
*/
- invokeElementMethod(location: RenderElementRef, methodName: string, args: Array) {
+ invokeElementMethod(renderElement: util.NgView, methodName: string, args: Array) {
console.log("NativeScriptRenderer.invokeElementMethod " + methodName + " " + args);
}
- setText(viewRef: RenderViewRef, textNodeIndex: number, text: string) {
- console.log("NativeScriptRenderer.setText ");
- }
-
- setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher) {
- console.log("NativeScriptRenderer.setEventDispatcher ");
- var view = resolveInternalDomView(viewRef);
- view.eventDispatcher = dispatcher;
- }
-
- private _componentTemplates: Map = new Map();
-
- public registerComponentTemplate(template: RenderComponentTemplate) {
- console.log('NativeScriptRenderer.registerComponentTemplate: ' + template.id);
- this._componentTemplates.set(template.id, template);
- }
-
- public resolveComponentTemplate(templateId: string): RenderComponentTemplate {
- console.log('NativeScriptRenderer.resolveComponentTemplate: ' + templateId);
- return this._componentTemplates.get(templateId);
- }
-
- public createRootContentInsertionPoint(): ViewNode {
- return this.createTemplateAnchor([]);
+ setText(renderNode: any, text: string) {
+ console.log("NativeScriptRenderer.setText");
}
- public createTemplateAnchor(attrNameAndValues: string[]): ViewNode {
+ public createTemplateAnchor(parentElement: util.NgView): util.NgView {
console.log('NativeScriptRenderer.createTemplateAnchor');
- return new ViewNode(null, 'template', attrNameAndValues);
- }
-
- public createElement(name: string, attrNameAndValues: string[]): ViewNode {
- console.log('NativeScriptRenderer.createElement: ' + name);
- return new ViewNode(null, name, attrNameAndValues);
+ return util.createTemplateAnchor(parentElement);
}
- public mergeElement(existing: ViewNode, attrNameAndValues: string[]){
- console.log('NativeScriptRenderer.mergeElement: ' + existing.viewName);
- existing.clearChildren();
- existing.setAttributeValues(attrNameAndValues);
+ public createElement(parentElement: util.NgView, name: string): util.NgView {
+ console.log('NativeScriptRenderer.createElement: ' + name + ' parent: ' + parentElement + ', ' + (parentElement ? parentElement.nodeName : 'null'));
+ return util.createView(name, parentElement);
}
- public createShadowRoot(host: ViewNode, templateId: string): ViewNode {
- throw new Error('Not implemented.');
- }
-
- public createText(value: string): ViewNode {
+ public createText(value: string): util.NgView {
console.log('NativeScriptRenderer.createText');
- return new DummyViewNode(null);
- }
-
- public appendChild(parent: ViewNode, child: ViewNode) {
- console.log('NativeScriptRenderer.appendChild: ' + parent.viewName + ' -> ' + child.viewName);
- parent.appendChild(child);
+ //No text nodes in NativeScript
+ return null;
}
- public on(element: ViewNode, eventName: string, callback: Function) {
- console.log('NativeScriptRenderer.on: ' + eventName);
+ public listen(renderElement: util.NgView, eventName: string, callback: Function) {
+ console.log('NativeScriptRenderer.listen: ' + eventName);
let zonedCallback = global.zone.bind(callback);
- element.on(eventName, zonedCallback);
+ renderElement.on(eventName, zonedCallback);
}
- public globalOn(target: string, eventName: string, callback: Function): Function {
+ public listenGlobal(target: string, eventName: string, callback: Function): Function {
throw new Error('Not implemented.');
}
}
-
-function resolveInternalDomView(viewRef: RenderViewRef): DefaultRenderView {
- return >viewRef;
-}
-
-function resolveBoundNode(elementRef: RenderElementRef): ViewNode {
- let view = resolveInternalDomView(elementRef.renderView);
- //Using an Angular internal API to get the index of the bound element.
- let internalBoundIndex = (elementRef).boundElementIndex;
- return view.boundElements[internalBoundIndex]
-}
-
-function resolveInternalDomFragment(fragmentRef: RenderFragmentRef): ViewNode[] {
- return (>fragmentRef).nodes;
-}
diff --git a/src/nativescript-angular/view-container.ts b/src/nativescript-angular/view-container.ts
new file mode 100644
index 000000000..67b82696e
--- /dev/null
+++ b/src/nativescript-angular/view-container.ts
@@ -0,0 +1,106 @@
+import {
+ NgView,
+ ViewExtensions,
+ getChildIndex,
+ insertChild,
+ removeChild
+} from "./view-util";
+import {ContentView} from "ui/content-view";
+
+//var console = {log: function(msg) {}}
+
+export class ViewContainer extends ContentView implements ViewExtensions {
+ public nodeName = "ViewContainer";
+ public templateParent: NgView = null;
+ public cssClasses = new Map();
+
+ public children: Array = [];
+
+ private _anchorIndex = -1;
+ private _visualParent: NgView = null;
+
+ constructor() {
+ super();
+ this.on('unloaded', () => this.clearChildren());
+ }
+
+ public addedToView() {
+ //Invalidate anchor index.
+ this._anchorIndex = -1;
+ this._visualParent = null;
+ this.attachChildrenToParent();
+ }
+
+ private get anchorIndex(): number{
+ if (this._anchorIndex === -1) {
+ this._anchorIndex = getChildIndex(this.parent, this);
+ console.log('Got anchorIndex: ' + this._anchorIndex + ' - ' + this.parent + ' - ' + this + ' --- ' + (this.parent)._subViews);
+ }
+ return this._anchorIndex;
+ }
+
+ private get visualParent(): NgView {
+ if (this._visualParent === null) {
+ if (this.parent instanceof ViewContainer) {
+ this._visualParent = (this.parent).visualParent;
+ } else {
+ this._visualParent = this.parent;
+ }
+ }
+ return this._visualParent;
+ }
+
+ private getParentIndex(childIndex: number): number {
+ if (childIndex !== -1) {
+ return this.anchorIndex + childIndex;
+ } else {
+ return this.anchorIndex + this.children.length;
+ }
+ }
+
+ public getChildIndex(child: NgView) {
+ return this.children.indexOf(child);
+ }
+
+ insertChild(child: NgView, atIndex = -1) {
+ console.log('insert component child: ' + child + ', ' + atIndex);
+ if (atIndex !== -1) {
+ this.children.splice(atIndex, 0, child);
+ } else {
+ this.children.push(child);
+ }
+
+ if (this.parent) {
+ const parentIndex = this.getParentIndex(atIndex);
+ console.log('actually inserting in: ' + this.parent + ', ' + parentIndex);
+ insertChild(this.parent, child, parentIndex)
+ child.templateParent = this;
+ }
+ }
+
+ private attachChildToParent(child: NgView, atIndex: number) {
+ const parentIndex = this.getParentIndex(atIndex);
+ console.log('attaching: ' + child + ' to: ' + this.parent + ' at ' + parentIndex + '/' + atIndex);
+ insertChild(this.parent, child, parentIndex)
+ }
+
+ private attachChildrenToParent() {
+ this.children.forEach((child, index) => {
+ if (!child.parent) {
+ console.log('attaching pending child: ' + this + ' - ' + child);
+ this.attachChildToParent(child, index);
+ }
+ });
+ }
+
+ removeChild(child: NgView) {
+ this.children = this.children.filter((item) => item !== child);
+ removeChild(this.parent, child);
+ }
+
+ clearChildren() {
+ while (this.children.length > 0) {
+ this.removeChild(this.children[0]);
+ }
+ }
+}
diff --git a/src/nativescript-angular/view-util.ts b/src/nativescript-angular/view-util.ts
new file mode 100644
index 000000000..797a14e49
--- /dev/null
+++ b/src/nativescript-angular/view-util.ts
@@ -0,0 +1,232 @@
+import {isString} from "utils/types";
+import {View, AddChildFromBuilder} from "ui/core/view";
+import {Placeholder} from "ui/placeholder";
+import {ContentView} from 'ui/content-view';
+import {LayoutBase} from 'ui/layouts/layout-base';
+import {ViewClass, getViewClass, isKnownView} from './element-registry';
+import {ViewContainer} from './view-container';
+import {getSpecialPropertySetter} from "ui/builder/special-properties";
+
+//var console = {log: function(msg) {}}
+
+export interface ViewExtensions {
+ nodeName: string;
+ templateParent: NgView;
+ cssClasses: Map;
+}
+
+export type NgView = View & ViewExtensions;
+export type NgLayoutBase = LayoutBase & ViewExtensions;
+export type NgContentView = ContentView & ViewExtensions;
+export type NgAddChildFromBuilder = View & AddChildFromBuilder & ViewExtensions;
+
+function isView(view: any): view is NgView {
+ return view instanceof View;
+}
+
+function isLayout(view: any): view is NgLayoutBase {
+ return view instanceof LayoutBase;
+}
+
+function isContentView(view: any): view is NgContentView {
+ return view instanceof ContentView;
+}
+
+function hasBuilderHooks(view: any): view is NgAddChildFromBuilder {
+ return view._addChildFromBuilder !== undefined;
+}
+
+function isViewContainer(view: any): view is ViewContainer {
+ return view instanceof ViewContainer;
+}
+
+function isComplexProperty(view: NgView) {
+ const name = view.nodeName
+ return isString(name) && name.indexOf(".") !== -1;
+}
+
+export function insertChild(parent: any, child: NgView, atIndex = -1) {
+ if (child.nodeName === 'Switch') {
+ console.log('inserting switch into: ' + parent);
+ }
+
+ if (isLayout(parent)) {
+ if (atIndex !== -1) {
+ parent.insertChild(child, atIndex);
+ } else {
+ parent.addChild(child);
+ }
+ } else if (isViewContainer(parent)) {
+ parent.insertChild(child, atIndex);
+ } else if (isContentView(parent)) {
+ parent.content = child;
+ } else if (hasBuilderHooks(parent)) {
+ parent._addChildFromBuilder(this.viewName, this.nativeView);
+ } else {
+ throw new Error("Parent can't contain children: " + parent.nodeName + ', ' + parent);
+ }
+ if (child instanceof ViewContainer) {
+ (child).addedToView();
+ }
+}
+
+export function removeChild(parent: any, child: NgView) {
+ if (isLayout(parent)) {
+ parent.removeChild(child);
+ } else if (isViewContainer(parent)) {
+ parent.removeChild(child);
+ } else if (isContentView(parent)) {
+ if (parent.content === child) {
+ parent.content = null;
+ }
+ } else if (isView(parent)) {
+ parent._removeView(child);
+ } else {
+ throw new Error('Unknown parent type: ' + parent);
+ }
+}
+
+export function getChildIndex(parent: any, child: NgView) {
+ if (isLayout(parent)) {
+ return parent.getChildIndex(child);
+ } else if (isViewContainer(parent)) {
+ return parent.getChildIndex(child);
+ } else if (isContentView(parent)) {
+ return child === parent.content ? 0 : -1;
+ } else {
+ throw new Error("Parent can't contain children: " + parent.nodeName + ', ' + parent);
+ }
+}
+
+function createAndAttach(name: string, viewClass: ViewClass, parent: NgView): NgView {
+ const view = new viewClass();
+ view.nodeName = name;
+ if (parent) {
+ insertChild(parent, view);
+ }
+ return view;
+}
+
+export function createView(name: string, parent: NgView): NgView {
+ if (isKnownView(name)) {
+ const viewClass = getViewClass(name);
+ return createAndAttach(name, viewClass, parent);
+ } else {
+ return createViewContainer(parent);
+ }
+}
+
+export function createText(value: string): NgView {
+ const text = new Placeholder();
+ text.nodeName = "#text";
+ text.visibility = "collapse";
+ return text;
+}
+
+export function createTemplateContainer(parsedNode: NgView) {
+ const parent = parsedNode.parent;
+ const templateContainer = createView('StackLayout', parent);
+ //templateContainer.visibility = "collapse";
+ return templateContainer;
+}
+
+export function createViewContainer(parentElement: NgView) {
+ //HACK: Using a ContentView here, so that it creates a native View object
+ console.log('Creating view container in:' + parentElement);
+ const anchor = createAndAttach('ViewContainer', ViewContainer, parentElement);
+ anchor.visibility = "collapse";
+ return anchor;
+}
+
+export function createTemplateAnchor(parentElement: NgView) {
+ //HACK: Using a ContentView here, so that it creates a native View object
+ const anchor = createAndAttach('ContentView', ContentView, parentElement);
+ anchor.visibility = "collapse";
+ return anchor;
+}
+
+function isXMLAttribute(name: string): boolean {
+ switch (name) {
+ case "style": return true;
+ case "rows": return true;
+ case "columns": return true;
+ case "fontAttributes": return true;
+ default: return false;
+ }
+}
+
+export function setProperty(view: NgView, attributeName: string, value: any): void {
+ console.log('Setting attribute: ' + attributeName);
+
+ let specialSetter = getSpecialPropertySetter(attributeName);
+ let propMap = getProperties(view);
+
+ if (attributeName === "class") {
+ this.setClasses(value);
+ } else if (isXMLAttribute(attributeName)) {
+ view._applyXmlAttribute(attributeName, value);
+ } else if (specialSetter) {
+ specialSetter(view, value);
+ } else if (propMap.has(attributeName)) {
+ // We have a lower-upper case mapped property.
+ let propertyName = propMap.get(attributeName);
+ view[propertyName] = value;
+ } else {
+ // Unknown attribute value -- just set it to our object as is.
+ view[attributeName] = value;
+ }
+}
+
+const propertyMaps: Map> = new Map>();
+
+function getProperties(instance: any): Map {
+ let type = instance && instance.constructor;
+ if (!type) {
+ return new Map();
+ }
+
+ if (!propertyMaps.has(type)) {
+ var propMap = new Map();
+ for (var propName in instance) {
+ propMap.set(propName.toLowerCase(), propName);
+ }
+ propertyMaps.set(type, propMap);
+ }
+ return propertyMaps.get(type);
+}
+
+
+function cssClasses(view: NgView) {
+ if (!view.cssClasses) {
+ view.cssClasses = new Map();
+ }
+ return view.cssClasses;
+}
+
+export function addClass(view: NgView, className: string): void {
+ cssClasses(view).set(className, true);
+ syncClasses(view);
+}
+
+export function removeClass(view: NgView, className: string): void {
+ cssClasses(view).delete(className);
+ syncClasses(view);
+}
+
+const whiteSpaceSplitter = /\s+/;
+
+function setClasses(view: NgView, classesValue: string): void {
+ let classes = classesValue.split(whiteSpaceSplitter)
+ classes.forEach((className) => cssClasses(view).set(className, true));
+ syncClasses(view);
+}
+
+function syncClasses(view: NgView): void {
+ let classValue = (Array).from(cssClasses(view).keys()).join(' ');
+ view.cssClass = classValue;
+}
+
+
+export function setStyleProperty(view: NgView, styleName: string, styleValue: string): void {
+ throw new Error("Not implemented: setStyleProperty");
+}
diff --git a/src/nativescript-angular/view_node.ts b/src/nativescript-angular/view_node.ts
deleted file mode 100644
index 1e53f0faa..000000000
--- a/src/nativescript-angular/view_node.ts
+++ /dev/null
@@ -1,411 +0,0 @@
-import {View} from 'ui/core/view';
-import {ContentView} from 'ui/content-view';
-import {Observable, EventData} from 'data/observable';
-import {getSpecialPropertySetter} from "ui/builder/special-properties";
-import {topmost} from 'ui/frame';
-import {Button} from 'ui/button';
-import {StackLayout} from 'ui/layouts/stack-layout';
-import {DockLayout} from 'ui/layouts/dock-layout';
-import {Label} from 'ui/label';
-import {TextField} from 'ui/text-field';
-import {TextView} from 'ui/text-view';
-import {Switch} from 'ui/switch';
-import {LayoutBase} from 'ui/layouts/layout-base';
-import gestures = require("ui/gestures");
-import {ViewClass, getViewClass, isKnownView} from './element-registry';
-import {isString} from "utils/types";
-
-type EventHandler = (args: EventData) => void;
-
-//var console = {log: function(msg) {}}
-
-export class ViewNode {
- private eventListeners: Map = new Map();
-
- public nativeView: View;
- private _parentView: View;
- private _attachedToView: boolean = false;
- private attributes: Map = new Map();
- private cssClasses: Map = new Map();
- private static whiteSpaceSplitter = /\s+/;
-
- public children: Array = [];
-
- constructor(public parentNode: ViewNode,
- public viewName: string,
- attrNameAndValues: string[]) {
- this.setAttributeValues(attrNameAndValues);
-
- if (this.parentNode)
- this.parentNode.children.push(this);
- }
-
- print(depth: number = 0) {
- let line = "";
-
- for (let i = 0; i < depth; i++)
- line += " "
-
- console.log(line + this.viewName + '(' + this._attachedToView + ') ');
-
- this.children.forEach(child => {
- child.print(depth + 1);
- });
- }
-
- get parentNativeView(): View {
- if (this._parentView)
- return this._parentView
-
- if (this.parentNode) {
- if (this.parentNode.viewName !== "template" && this.parentNode.nativeView) {
- this._parentView = this.parentNode.nativeView;
- } else {
- this._parentView = this.parentNode.parentNativeView;
- }
- }
- if (!this._parentView) {
- this._parentView = topmost().currentPage;
- }
- return this._parentView;
- }
-
- get isComplexProperty(): boolean {
- return ViewNode.isComplexProperty(this.viewName);
- }
-
- public attachToView(atIndex: number = -1) {
- console.log('ViewNode.attachToView ' + this.viewName);
- if (this._attachedToView) {
- console.log('already attached.');
- return;
- }
-
- this._attachedToView = true;
-
- this.createUI(atIndex);
- this.attachUIEvents();
-
- this.children.forEach(child => {
- child.attachToView();
- });
-
- this.postAttachUI();
- }
-
- private createUI(attachAtIndex: number): boolean {
- if (!isKnownView(this.viewName))
- return;
-
- console.log('createUI: ' + this.viewName +
- ', attachAt: ' + attachAtIndex +
- ', parent: ' + this.parentNode.viewName +
- ', parent UI ' + (this.parentNativeView.constructor).name);
-
- let viewClass = getViewClass(this.viewName);
- if (!this.nativeView) {
- this.nativeView = new viewClass();
- } else {
- console.log('Reattaching old view: ' + this.viewName);
- }
-
- this.configureUI();
-
- if (this.parentNativeView instanceof LayoutBase) {
- let parentLayout = this.parentNativeView;
- if (attachAtIndex != -1) {
- console.log('Layout.insertChild');
- let indexOffset = 0;
- if (this.parentNode.viewName === "template") {
- indexOffset = parentLayout.getChildIndex(this.parentNode.nativeView);
- }
- parentLayout.insertChild(this.nativeView, indexOffset + attachAtIndex);
- } else {
- parentLayout.addChild(this.nativeView);
- }
- } else if (this.parentNativeView instanceof ContentView) {
- (this.parentNativeView).content = this.nativeView;
- } else if ((this.parentNativeView)._addChildFromBuilder) {
- (this.parentNativeView)._addChildFromBuilder(this.viewName, this.nativeView);
- } else if (this.parentNode.isComplexProperty) {
- // complex property - we will deal with this in postAttachUI()
- }
- else {
- console.log('parentNativeView: ' + this.parentNativeView);
- throw new Error("Parent view can't have children! " + this.parentNativeView);
- }
- }
-
- private postAttachUI() {
- if (this.isComplexProperty) {
- let nativeParent = this.parentNativeView;
- if (!nativeParent) {
- return;
- }
-
- let propertyName = ViewNode.getComplexPropertyName(this.viewName);
- let realChildren = [];
- for (let child of this.children) {
- if (child.nativeView) {
- realChildren.push(child.nativeView);
- }
- }
- if (realChildren.length > 0) {
- if (nativeParent._addArrayFromBuilder) {
- nativeParent._addArrayFromBuilder(propertyName, realChildren);
- }
- else {
- this.parentNode.setAttribute(propertyName, realChildren[0]);
- }
- }
- }
- }
-
- private static propertyMaps: Map> = new Map>();
-
- private static getProperties(instance: any): Map {
- let type = instance && instance.constructor;
- if (!type) {
- return new Map();
- }
-
- if (!ViewNode.propertyMaps.has(type)) {
- var propMap = new Map();
- for (var propName in instance) {
- propMap.set(propName.toLowerCase(), propName);
- }
- ViewNode.propertyMaps.set(type, propMap);
- }
- return ViewNode.propertyMaps.get(type);
- }
-
- private static isComplexProperty(name: string): boolean {
- return isString(name) && name.indexOf(".") !== -1;
- }
-
- private static getComplexPropertyName(fullName: string): string {
- var name: string;
-
- if (isString(fullName)) {
- var names = fullName.split(".");
- name = names[names.length - 1];
- }
-
- return name;
- }
-
- private configureUI() {
- if (this.attributes.size == 0)
- return;
-
- this.attributes.forEach((value, name) => {
- this.setAttribute(name, value);
- });
- this.syncClasses();
- }
-
- public setAttributeValues(attrNameAndValues: string[]) {
- if (attrNameAndValues) {
- for (let i = 0; i < attrNameAndValues.length; i += 2) {
- let name = attrNameAndValues[i];
- let value = attrNameAndValues[i + 1];
- this.setAttribute(name, value);
- }
- }
- }
-
- private isXMLAttribute(name: string): boolean {
- switch (name) {
- case "style": return true;
- case "rows": return true;
- case "columns": return true;
- case "fontAttributes": return true;
- default: return false;
- }
- }
-
- public setAttribute(attributeName: string, value: any): void {
- if (!this.nativeView) {
- console.log('Native view not created. Delaying attribute set: ' + attributeName);
- this.attributes.set(attributeName, value);
- return;
- }
-
- console.log('Setting attribute: ' + attributeName);
-
- let specialSetter = getSpecialPropertySetter(attributeName);
- let propMap = ViewNode.getProperties(this.nativeView);
-
- if (attributeName === "class") {
- this.setClasses(value);
- } else if (this.isXMLAttribute(attributeName)) {
- this.nativeView._applyXmlAttribute(attributeName, value);
- } else if (specialSetter) {
- specialSetter(this.nativeView, value);
- } else if (propMap.has(attributeName)) {
- // We have a lower-upper case mapped property.
- let propertyName = propMap.get(attributeName);
- this.nativeView[propertyName] = value;
- } else {
- // Unknown attribute value -- just set it to our object as is.
- this.nativeView[attributeName] = value;
- }
- }
-
- public setStyleProperty(styleName: string, styleValue: string): void {
- throw new Error("Not implemented: setStyleProperty");
- }
-
- private attachUIEvents() {
- if (!this.nativeView) {
- return;
- }
-
- console.log('ViewNode.attachUIEvents: ' + this.viewName + ' ' + this.eventListeners.size);
- this.eventListeners.forEach((callback, eventName) => {
- this.attachNativeEvent(eventName, callback);
- });
- }
-
- private detachUIEvents() {
- if (!this.nativeView) {
- return;
- }
-
- console.log('ViewNode.detachUIEvents: ' + this.viewName + ' ' + this.eventListeners.size);
- this.eventListeners.forEach((callback, eventName) => {
- this.detachNativeEvent(eventName, callback);
- });
- }
-
- private resolveNativeEvent(parsedEventName: string): string {
- //TODO: actually resolve the event...
- return parsedEventName;
- }
-
- private isGesture(eventName: string): boolean {
- return gestures.fromString(name.toLowerCase()) !== undefined;
- }
-
- public on(eventName, callback) {
- console.log('ViewNode.on: ' + this.viewName + ' -> ' + eventName);
- if (!this.nativeView) {
- this.eventListeners.set(eventName, callback);
- } else {
- this.attachNativeEvent(eventName, callback);
- }
- }
-
- private attachNativeEvent(eventName, callback) {
- console.log('attachNativeEvent ' + eventName);
- let resolvedEvent = this.resolveNativeEvent(eventName);
- this.nativeView.addEventListener(resolvedEvent, callback);
- }
-
- private detachNativeEvent(eventName, callback) {
- console.log('detachNativeEvent ' + eventName);
- let resolvedEvent = this.resolveNativeEvent(eventName);
- this.nativeView.removeEventListener(resolvedEvent, callback);
- }
-
- public appendChild(childNode: ViewNode) {
- this.insertChildAt(this.children.length, childNode);
- }
-
- public insertChildAt(index: number, childNode: ViewNode): void {
- console.log('ViewNode.insertChildAt: ' + this.viewName + ' ' + index + ' ' + childNode.viewName);
- if (childNode.parentNode) {
- console.log('Moving child to new parent');
- childNode.parentNode.removeChild(childNode);
- }
- this.children.splice(index, 0, childNode);
- childNode.parentNode = this;
- }
-
- public removeChild(childNode: ViewNode): void {
- childNode.parentNode = null;
- childNode._parentView = null;
- this.children = this.children.filter((item) => item !== childNode);
-
- childNode.detachFromView();
- }
-
- public detachFromView(): void {
- this._attachedToView = false;
-
- this.detachUIEvents();
- if (this.nativeView) {
- let nativeParent = this.nativeView.parent;
- if (nativeParent) {
- if (nativeParent instanceof LayoutBase) {
- (nativeParent).removeChild(this.nativeView);
- } else if (nativeParent instanceof ContentView) {
- (nativeParent).content = undefined;
- }
- else {
- nativeParent._removeView(this.nativeView);
- }
- }
- }
-
- this.children.forEach((childNode) => {
- childNode.detachFromView();
- });
- }
-
- public clearChildren() {
- while (this.children.length > 0) {
- this.removeChild(this.children[0]);
- }
- }
-
- public getChildIndex(childNode: ViewNode) {
- return this.children.indexOf(childNode);
- }
-
- public setProperty(name: string, value: any) {
- console.log('ViewNode.setProperty ' + this.viewName + ' setProperty ' + name + ' ' + value);
- if (this.nativeView) {
- this.setAttribute(name, value);
- } else {
- console.log('setProperty called without a nativeView');
- }
- }
-
- public addClass(className: string): void {
- this.cssClasses.set(className, true);
- this.syncClasses();
- }
-
- public removeClass(className: string): void {
- this.cssClasses.delete(className);
- this.syncClasses();
- }
-
- public setClasses(classesValue: string): void {
- let classes = classesValue.split(ViewNode.whiteSpaceSplitter)
- classes.forEach((className) => this.cssClasses.set(className, true));
- this.syncClasses();
- }
-
- private syncClasses(): void {
- let classValue = (Array).from(this.cssClasses.keys()).join(' ');
- if (this.nativeView && classValue) {
- this.nativeView.cssClass = classValue;
- }
- }
-}
-
-export class DummyViewNode extends ViewNode {
- constructor(public parentNode: ViewNode) {
- super(parentNode, null, []);
- }
- public attachToView(atIndex: number = -1) {
- }
- public insertChildAt(index: number, childNode: ViewNode) {
- }
- public removeChild(childNode: ViewNode) {
- }
- setProperty(name: string, value: any) {
- }
-}