diff --git a/ng-sample/app/app.ts b/ng-sample/app/app.ts
index fe26d72d3..40131f25a 100644
--- a/ng-sample/app/app.ts
+++ b/ng-sample/app/app.ts
@@ -24,6 +24,7 @@ import {ImageTest} from "./examples/image/image-test";
import {NavigationTest} from "./examples/navigation/navigation-test";
import {ActionBarTest} from "./examples/action-bar/action-bar-test";
import {ModalTest} from "./examples/modal/modal-test";
+import {PlatfromDirectivesTest} from "./examples/platform-directives/platform-directives-test";
nativeScriptBootstrap(RendererTest);
//nativeScriptBootstrap(Benchmark);
@@ -34,5 +35,6 @@ nativeScriptBootstrap(RendererTest);
//nativeScriptBootstrap(ListTestAsync);
//nativeScriptBootstrap(ImageTest);
//nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS]);
-//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS], { startPageActionBarHidden: false });
+// nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS], { startPageActionBarHidden: false });
// nativeScriptBootstrap(ModalTest);
+// nativeScriptBootstrap(PlatfromDirectivesTest);
diff --git a/ng-sample/app/examples/platform-directives/platform-directives-test.ts b/ng-sample/app/examples/platform-directives/platform-directives-test.ts
new file mode 100644
index 000000000..3ed8f1156
--- /dev/null
+++ b/ng-sample/app/examples/platform-directives/platform-directives-test.ts
@@ -0,0 +1,18 @@
+import {Component} from 'angular2/core';
+
+@Component({
+ selector: 'action-bar-test',
+ template: `
+
+
+
+
+
+ `
+})
+export class PlatfromDirectivesTest {
+}
+
+
diff --git a/ng-sample/package.json b/ng-sample/package.json
index 0cc2915a2..eea477e00 100644
--- a/ng-sample/package.json
+++ b/ng-sample/package.json
@@ -23,7 +23,7 @@
},
"homepage": "https://github.com/NativeScript/template-hello-world",
"dependencies": {
- "tns-core-modules": "^2.0.0-angular-1",
+ "tns-core-modules": "^2.0.0-angular-2",
"nativescript-intl": "^0.0.2",
"angular2": "2.0.0-beta.9",
"parse5": "1.4.2",
diff --git a/package.json b/package.json
index 03573b020..22d0d4ebd 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
},
"scripts": {},
"dependencies": {
- "tns-core-modules": "^2.0.0-angular-1",
+ "tns-core-modules": "^2.0.0-angular-2",
"nativescript-intl": "^0.0.2",
"angular2": "2.0.0-beta.9",
"es6-shim": "^0.33.3",
diff --git a/src/nativescript-angular/application.ts b/src/nativescript-angular/application.ts
index 38bdb4391..e59cfc419 100644
--- a/src/nativescript-angular/application.ts
+++ b/src/nativescript-angular/application.ts
@@ -39,7 +39,7 @@ import application = require('application');
export type ProviderArray = Array;
-import {defaultPageProvider} from "./platform-providers";
+import {defaultPageProvider, defaultDeviceProvider} from "./platform-providers";
import * as nativescriptIntl from "nativescript-intl";
global.Intl = nativescriptIntl;
@@ -68,6 +68,7 @@ export function bootstrap(appComponentType: any,
provide(ExceptionHandler, {useFactory: () => new ExceptionHandler(DOM, true), deps: []}),
defaultPageProvider,
+ defaultDeviceProvider,
NativeScriptRootRenderer,
provide(RootRenderer, {useClass: NativeScriptRootRenderer}),
NativeScriptRenderer,
diff --git a/src/nativescript-angular/directives/ns-directives.ts b/src/nativescript-angular/directives/ns-directives.ts
index 670d847be..70f18cb41 100644
--- a/src/nativescript-angular/directives/ns-directives.ts
+++ b/src/nativescript-angular/directives/ns-directives.ts
@@ -8,6 +8,7 @@ import {NumberValueAccessor} from '../value-accessors/number-value-accessor';
import {SelectedIndexValueAccessor} from '../value-accessors/selectedIndex-value-accessor';
import {TabViewDirective, TabViewItemDirective} from './tab-view';
import {ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective} from './action-bar';
+import {AndroidFilterComponent, IosFilterComponent} from './platform-filters';
export const NS_DIRECTIVES: Type[] = [
ListViewComponent,
@@ -22,5 +23,7 @@ export const NS_DIRECTIVES: Type[] = [
ActionBarComponent,
ActionBarScope,
ActionItemDirective,
- NavigationButtonDirective
+ NavigationButtonDirective,
+ AndroidFilterComponent,
+ IosFilterComponent
];
diff --git a/src/nativescript-angular/directives/platform-filters.ts b/src/nativescript-angular/directives/platform-filters.ts
new file mode 100644
index 000000000..8e6dd8e56
--- /dev/null
+++ b/src/nativescript-angular/directives/platform-filters.ts
@@ -0,0 +1,25 @@
+import {Component, Inject} from 'angular2/core';
+import {Device, platformNames} from "platform";
+import {DEVICE} from "../platform-providers";
+
+@Component({
+ selector: "android",
+ template: ``,
+})
+export class AndroidFilterComponent {
+ public show: boolean;
+ constructor( @Inject(DEVICE) device: Device) {
+ this.show = (device.os === platformNames.android);
+ }
+}
+
+@Component({
+ selector: "ios",
+ template: ``,
+})
+export class IosFilterComponent {
+ public show: boolean;
+ constructor( @Inject(DEVICE) device: Device) {
+ this.show = (device.os === platformNames.ios);
+ }
+}
\ No newline at end of file
diff --git a/src/nativescript-angular/platform-providers.ts b/src/nativescript-angular/platform-providers.ts
index 3c3645cf2..a81f93d3d 100644
--- a/src/nativescript-angular/platform-providers.ts
+++ b/src/nativescript-angular/platform-providers.ts
@@ -1,10 +1,12 @@
import {topmost} from 'ui/frame';
import {Page} from 'ui/page';
import {provide, Provider, OpaqueToken} from 'angular2/src/core/di';
+import {Device, ScreenMetrics, device, screen} from "platform";
export const APP_ROOT_VIEW = new OpaqueToken('App Root View');
+export const DEVICE = new OpaqueToken('platfrom device');
-export const defaultPageProvider = provide(Page, {useFactory: getDefaultPage});
+export const defaultPageProvider = provide(Page, { useFactory: getDefaultPage });
export function getDefaultPage(): Page {
const frame = topmost();
@@ -14,3 +16,6 @@ export function getDefaultPage(): Page {
return null;
}
}
+
+export const defaultDeviceProvider = provide(DEVICE, { useValue: device });
+
diff --git a/src/nativescript-angular/renderer.ts b/src/nativescript-angular/renderer.ts
index 081fb4c91..a787b16b2 100644
--- a/src/nativescript-angular/renderer.ts
+++ b/src/nativescript-angular/renderer.ts
@@ -5,7 +5,7 @@ import {
RenderComponentType,
RenderDebugInfo
} from 'angular2/src/core/render/api';
-import {APP_ROOT_VIEW} from "./platform-providers";
+import {APP_ROOT_VIEW, DEVICE} from "./platform-providers";
import {isBlank} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {COMPONENT_VARIABLE, CONTENT_ATTR} from 'angular2/src/platform/dom/dom_renderer';
@@ -13,16 +13,20 @@ import {View} from "ui/core/view";
import * as application from "application";
import {topmost} from 'ui/frame';
import {Page} from 'ui/page';
-import * as util from "./view-util";
+import {traceLog, ViewUtil, NgView} from "./view-util";
import {escapeRegexSymbols} from "utils/utils";
+import { Device } from "platform";
export { rendererTraceCategory } from "./view-util";
@Injectable()
export class NativeScriptRootRenderer implements RootRenderer {
private _rootView: View = null;
- constructor(@Optional() @Inject(APP_ROOT_VIEW) rootView: View) {
+ private _viewUtil: ViewUtil;
+
+ constructor( @Optional() @Inject(APP_ROOT_VIEW) rootView: View, @Inject(DEVICE) device: Device) {
this._rootView = rootView;
+ this._viewUtil = new ViewUtil(device);
}
private _registeredComponents: Map = new Map();
@@ -38,6 +42,10 @@ export class NativeScriptRootRenderer implements RootRenderer {
return this.rootView.page;
}
+ public get viewUtil(): ViewUtil {
+ return this._viewUtil;
+ }
+
renderComponent(componentProto: RenderComponentType): Renderer {
var renderer = this._registeredComponents.get(componentProto.id);
if (isBlank(renderer)) {
@@ -53,19 +61,24 @@ export class NativeScriptRenderer extends Renderer {
private componentProtoId: string;
private hasComponentStyles: boolean;
private rootRenderer: NativeScriptRootRenderer;
+
+ private get viewUtil(): ViewUtil {
+ return this.rootRenderer.viewUtil;
+ }
+
constructor(private _rootRenderer: NativeScriptRootRenderer, private componentProto: RenderComponentType) {
super();
this.rootRenderer = _rootRenderer;
let page = this.rootRenderer.page;
let stylesLength = componentProto.styles.length;
this.componentProtoId = componentProto.id;
- for(let i = 0; i < stylesLength; i++) {
+ for (let i = 0; i < stylesLength; i++) {
this.hasComponentStyles = true;
let cssString = componentProto.styles[i] + "";
const realCSS = this.replaceNgAttribute(cssString, this.componentProtoId);
application.addCss(realCSS);
}
- util.traceLog('NativeScriptRenderer created');
+ traceLog('NativeScriptRenderer created');
}
private attrReplacer = new RegExp(escapeRegexSymbols(CONTENT_ATTR), "g");
@@ -73,120 +86,120 @@ export class NativeScriptRenderer extends Renderer {
private replaceNgAttribute(input: string, componentId: string): string {
return input.replace(this.attrReplacer,
- "_ng_content_" + componentId.replace(this.attrSanitizer, "_"));
+ "_ng_content_" + componentId.replace(this.attrSanitizer, "_"));
}
renderComponent(componentProto: RenderComponentType): Renderer {
return this._rootRenderer.renderComponent(componentProto);
}
- selectRootElement(selector: string): util.NgView {
- util.traceLog('selectRootElement: ' + selector);
- const rootView = this.rootRenderer.rootView;
+ selectRootElement(selector: string): NgView {
+ traceLog('selectRootElement: ' + selector);
+ const rootView = this.rootRenderer.rootView;
rootView.nodeName = 'ROOT';
return rootView;
}
- createViewRoot(hostElement: util.NgView): util.NgView {
- util.traceLog('CREATE VIEW ROOT: ' + hostElement.nodeName);
+ createViewRoot(hostElement: NgView): NgView {
+ traceLog('CREATE VIEW ROOT: ' + hostElement.nodeName);
return hostElement;
}
- projectNodes(parentElement: util.NgView, nodes: util.NgView[]): void {
- util.traceLog('NativeScriptRenderer.projectNodes');
+ projectNodes(parentElement: NgView, nodes: NgView[]): void {
+ traceLog('NativeScriptRenderer.projectNodes');
nodes.forEach((node) => {
- util.insertChild(parentElement, node);
+ this.viewUtil.insertChild(parentElement, node);
});
}
- attachViewAfter(anchorNode: util.NgView, viewRootNodes: util.NgView[]) {
- util.traceLog('NativeScriptRenderer.attachViewAfter: ' + anchorNode.nodeName + ' ' + anchorNode);
- const parent = (anchorNode.parent || anchorNode.templateParent);
- const insertPosition = util.getChildIndex(parent, anchorNode);
+ attachViewAfter(anchorNode: NgView, viewRootNodes: NgView[]) {
+ traceLog('NativeScriptRenderer.attachViewAfter: ' + anchorNode.nodeName + ' ' + anchorNode);
+ const parent = (anchorNode.parent || anchorNode.templateParent);
+ const insertPosition = this.viewUtil.getChildIndex(parent, anchorNode);
viewRootNodes.forEach((node, index) => {
const childIndex = insertPosition + index + 1;
- util.insertChild(parent, node, childIndex);
+ this.viewUtil.insertChild(parent, node, childIndex);
this.animateNodeEnter(node);
});
}
- detachView(viewRootNodes: util.NgView[]) {
- util.traceLog('NativeScriptRenderer.detachView');
+ detachView(viewRootNodes: NgView[]) {
+ traceLog('NativeScriptRenderer.detachView');
for (var i = 0; i < viewRootNodes.length; i++) {
var node = viewRootNodes[i];
- util.removeChild(node.parent, node);
+ this.viewUtil.removeChild(node.parent, node);
this.animateNodeLeave(node);
}
}
- animateNodeEnter(node: util.NgView) {
+ animateNodeEnter(node: NgView) {
}
- animateNodeLeave(node: util.NgView) {
+ animateNodeLeave(node: NgView) {
}
- public destroyView(hostElement: util.NgView, viewAllNodes: util.NgView[]) {
- util.traceLog("NativeScriptRenderer.destroyView");
+ public destroyView(hostElement: NgView, viewAllNodes: NgView[]) {
+ traceLog("NativeScriptRenderer.destroyView");
// Seems to be called on component dispose only (router outlet)
//TODO: handle this when we resolve routing and navigation.
}
- setElementProperty(renderElement: util.NgView, propertyName: string, propertyValue: any) {
- util.traceLog("NativeScriptRenderer.setElementProperty " + renderElement + ': ' + propertyName + " = " + propertyValue);
- util.setProperty(renderElement, propertyName, propertyValue);
+ setElementProperty(renderElement: NgView, propertyName: string, propertyValue: any) {
+ traceLog("NativeScriptRenderer.setElementProperty " + renderElement + ': ' + propertyName + " = " + propertyValue);
+ this.viewUtil.setProperty(renderElement, propertyName, propertyValue);
}
- setElementAttribute(renderElement: util.NgView, attributeName: string, attributeValue: string) {
- util.traceLog("NativeScriptRenderer.setElementAttribute " + renderElement + ': ' + attributeName + " = " + attributeValue);
+ setElementAttribute(renderElement: NgView, attributeName: string, attributeValue: string) {
+ traceLog("NativeScriptRenderer.setElementAttribute " + renderElement + ': ' + attributeName + " = " + attributeValue);
return this.setElementProperty(renderElement, attributeName, attributeValue);
}
- setElementClass(renderElement: util.NgView, className: string, isAdd: boolean): void {
- util.traceLog("NativeScriptRenderer.setElementClass " + className + " - " + isAdd);
+ setElementClass(renderElement: NgView, className: string, isAdd: boolean): void {
+ traceLog("NativeScriptRenderer.setElementClass " + className + " - " + isAdd);
if (isAdd) {
- util.addClass(renderElement, className);
+ this.viewUtil.addClass(renderElement, className);
} else {
- util.removeClass(renderElement, className);
+ this.viewUtil.removeClass(renderElement, className);
}
}
- setElementStyle(renderElement: util.NgView, styleName: string, styleValue: string): void {
- util.setStyleProperty(renderElement, styleName, styleValue);
+ setElementStyle(renderElement: NgView, styleName: string, styleValue: string): void {
+ this.viewUtil.setStyleProperty(renderElement, styleName, styleValue);
}
/**
* Used only in debug mode to serialize property changes to comment nodes,
* such as placeholders.
*/
- setBindingDebugInfo(renderElement: util.NgView, propertyName: string, propertyValue: string): void {
- util.traceLog('NativeScriptRenderer.setBindingDebugInfo: ' + renderElement + ', ' + propertyName + ' = ' + propertyValue);
+ setBindingDebugInfo(renderElement: NgView, propertyName: string, propertyValue: string): void {
+ traceLog('NativeScriptRenderer.setBindingDebugInfo: ' + renderElement + ', ' + propertyName + ' = ' + propertyValue);
}
setElementDebugInfo(renderElement: any, info: RenderDebugInfo): void {
- util.traceLog('NativeScriptRenderer.setElementDebugInfo: ' + renderElement);
+ traceLog('NativeScriptRenderer.setElementDebugInfo: ' + renderElement);
}
/**
* Calls a method on an element.
*/
- invokeElementMethod(renderElement: util.NgView, methodName: string, args: Array) {
- util.traceLog("NativeScriptRenderer.invokeElementMethod " + methodName + " " + args);
+ invokeElementMethod(renderElement: NgView, methodName: string, args: Array) {
+ traceLog("NativeScriptRenderer.invokeElementMethod " + methodName + " " + args);
}
setText(renderNode: any, text: string) {
- util.traceLog("NativeScriptRenderer.setText");
+ traceLog("NativeScriptRenderer.setText");
}
- public createTemplateAnchor(parentElement: util.NgView): util.NgView {
- util.traceLog('NativeScriptRenderer.createTemplateAnchor');
- return util.createTemplateAnchor(parentElement);
+ public createTemplateAnchor(parentElement: NgView): NgView {
+ traceLog('NativeScriptRenderer.createTemplateAnchor');
+ return this.viewUtil.createTemplateAnchor(parentElement);
}
- public createElement(parentElement: util.NgView, name: string): util.NgView {
- util.traceLog('NativeScriptRenderer.createElement: ' + name + ' parent: ' + parentElement + ', ' + (parentElement ? parentElement.nodeName : 'null'));
- return util.createView(name, parentElement, (view) => {
+ public createElement(parentElement: NgView, name: string): NgView {
+ traceLog('NativeScriptRenderer.createElement: ' + name + ' parent: ' + parentElement + ', ' + (parentElement ? parentElement.nodeName : 'null'));
+ return this.viewUtil.createView(name, parentElement, (view) => {
// Set an attribute to the view to scope component-specific css.
// The property name is pre-generated by Angular.
if (this.hasComponentStyles) {
@@ -196,13 +209,13 @@ export class NativeScriptRenderer extends Renderer {
});
}
- public createText(value: string): util.NgView {
- util.traceLog('NativeScriptRenderer.createText');
- return util.createText(value);;
+ public createText(value: string): NgView {
+ traceLog('NativeScriptRenderer.createText');
+ return this.viewUtil.createText(value);;
}
- public listen(renderElement: util.NgView, eventName: string, callback: Function): Function {
- util.traceLog('NativeScriptRenderer.listen: ' + eventName);
+ public listen(renderElement: NgView, eventName: string, callback: Function): Function {
+ traceLog('NativeScriptRenderer.listen: ' + eventName);
let zonedCallback = (global).zone.bind(callback);
renderElement.on(eventName, zonedCallback);
return () => renderElement.off(eventName, zonedCallback);
diff --git a/src/nativescript-angular/view-util.ts b/src/nativescript-angular/view-util.ts
index d9550903e..f074be8e0 100644
--- a/src/nativescript-angular/view-util.ts
+++ b/src/nativescript-angular/view-util.ts
@@ -7,18 +7,22 @@ import {ViewClass, getViewClass, getViewMeta, isKnownView, ViewExtensions, NgVie
import {getSpecialPropertySetter} from "ui/builder/special-properties";
import { ActionBar, ActionItem, NavigationButton } from "ui/action-bar";
import trace = require("trace");
+import {device, platformNames, Device} from "platform";
+const IOS_PREFX: string = "@ios:";
+const ANDROID_PREFX: string = "@android:";
+const whiteSpaceSplitter = /\s+/;
export const rendererTraceCategory = "ns-renderer";
-export function traceLog(msg) {
- trace.write(msg, rendererTraceCategory);
-}
-
export type ViewExtensions = ViewExtensions;
export type NgView = NgView;
-
export type NgLayoutBase = LayoutBase & ViewExtensions;
export type NgContentView = ContentView & ViewExtensions;
+export type BeforeAttachAction = (view: View) => void;
+
+export function traceLog(msg) {
+ trace.write(msg, rendererTraceCategory);
+}
export function isView(view: any): view is NgView {
return view instanceof View;
@@ -32,228 +36,258 @@ export function isContentView(view: any): view is NgContentView {
return view instanceof ContentView;
}
-export function insertChild(parent: any, child: NgView, atIndex = -1) {
- if (!parent || child.meta.skipAddToDom) {
- return;
+const propertyMaps: Map> = new Map>();
+
+export class ViewUtil {
+ private isIos: boolean;
+ private isAndroid: boolean;
+
+ constructor(device: Device) {
+ this.isIos = device.os === platformNames.ios;
+ this.isAndroid = device.os === platformNames.android;
}
- if (parent.meta && parent.meta.insertChild) {
- parent.meta.insertChild(parent, child, atIndex);
- } else if (isLayout(parent)) {
- if (atIndex !== -1) {
- parent.insertChild(child, atIndex);
+ public insertChild(parent: any, child: NgView, atIndex = -1) {
+ if (!parent || child.meta.skipAddToDom) {
+ return;
+ }
+
+ if (parent.meta && parent.meta.insertChild) {
+ parent.meta.insertChild(parent, child, atIndex);
+ } else if (isLayout(parent)) {
+ if (atIndex !== -1) {
+ parent.insertChild(child, atIndex);
+ } else {
+ parent.addChild(child);
+ }
+ } else if (isContentView(parent)) {
+ parent.content = child;
+ } else if (parent && parent._addChildFromBuilder) {
+ parent._addChildFromBuilder(child.nodeName, child);
} else {
- parent.addChild(child);
+ //throw new Error("Parent can't contain children: " + parent.nodeName + ', ' + parent);
}
- } else if (isContentView(parent)) {
- parent.content = child;
- } else if (parent && parent._addChildFromBuilder) {
- parent._addChildFromBuilder(child.nodeName, child);
- } else {
- //throw new Error("Parent can't contain children: " + parent.nodeName + ', ' + parent);
}
-}
-export function removeChild(parent: any, child: NgView) {
- if (!parent || child.meta.skipAddToDom) {
- return;
+ public removeChild(parent: any, child: NgView) {
+ if (!parent || child.meta.skipAddToDom) {
+ return;
+ }
+
+ if (parent.meta && parent.meta.removeChild) {
+ parent.meta.removeChild(parent, child);
+ } else if (isLayout(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);
+ }
}
- if (parent.meta && parent.meta.removeChild) {
- parent.meta.removeChild(parent, child);
- } else if (isLayout(parent)) {
- parent.removeChild(child);
- } else if (isContentView(parent)) {
- if (parent.content === child) {
- parent.content = null;
+ public getChildIndex(parent: any, child: NgView) {
+ if (isLayout(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);
}
- } 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 (isContentView(parent)) {
- return child === parent.content ? 0 : -1;
- } else {
- //throw new Error("Parent can't contain children: " + parent);
+ private createAndAttach(name: string, viewClass: ViewClass, parent: NgView, beforeAttach?: BeforeAttachAction): NgView {
+ const view = new viewClass();
+ view.nodeName = name;
+ view.meta = getViewMeta(name);
+ if (beforeAttach) {
+ beforeAttach(view);
+ }
+ if (parent) {
+ this.insertChild(parent, view);
+ }
+ return view;
}
-}
-function createAndAttach(name: string, viewClass: ViewClass, parent: NgView, beforeAttach?: BeforeAttachAction): NgView {
- const view = new viewClass();
- view.nodeName = name;
- view.meta = getViewMeta(name);
- if (beforeAttach) {
- beforeAttach(view);
+ public createView(name: string, parent: NgView, beforeAttach?: BeforeAttachAction): NgView {
+ if (isKnownView(name)) {
+ const viewClass = getViewClass(name);
+ return this.createAndAttach(name, viewClass, parent, beforeAttach);
+ } else {
+ return this.createViewContainer(name, parent, beforeAttach);
+ }
}
- if (parent) {
- insertChild(parent, view);
+
+ public createText(value: string): NgView {
+ const text = new Placeholder();
+ text.nodeName = "#text";
+ text.visibility = "collapse";
+ text.meta = getViewMeta("Placeholder");
+ return text;
}
- return view;
-}
-export type BeforeAttachAction = (view: View) => void;
+ public createViewContainer(name: string, parentElement: NgView, beforeAttach: BeforeAttachAction) {
+ traceLog('Creating view container in:' + parentElement);
-export function createView(name: string, parent: NgView, beforeAttach?: BeforeAttachAction): NgView {
- if (isKnownView(name)) {
- const viewClass = getViewClass(name);
- return createAndAttach(name, viewClass, parent, beforeAttach);
- } else {
- return createViewContainer(name, parent, beforeAttach);
+ const layout = this.createView('ProxyViewContainer', parentElement, beforeAttach);
+ layout.nodeName = 'ProxyViewContainer';
+ return layout;
}
-}
-export function createText(value: string): NgView {
- const text = new Placeholder();
- text.nodeName = "#text";
- text.visibility = "collapse";
- text.meta = getViewMeta("Placeholder");
- return text;
-}
+ public createTemplateAnchor(parentElement: NgView) {
+ //HACK: Using a ContentView here, so that it creates a native View object
+ const anchor = this.createAndAttach('template', ContentView, parentElement);
+ anchor.visibility = "collapse";
+ anchor.templateParent = parentElement;
+ return anchor;
+ }
-export function createViewContainer(name: string, parentElement: NgView, beforeAttach: BeforeAttachAction) {
- traceLog('Creating view container in:' + parentElement);
+ 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;
+ }
+ }
- const layout = createView('ProxyViewContainer', parentElement, beforeAttach);
- layout.nodeName = 'ProxyViewContainer';
- return layout;
-}
+ private platformFilter(attribute: string): string {
+ var lowered = attribute.toLowerCase();
+ if (lowered.indexOf(IOS_PREFX) === 0) {
+ if (this.isIos) {
+ return attribute.substr(IOS_PREFX.length);
+ } else {
+ return null;
+ }
+ }
-export function createTemplateAnchor(parentElement: NgView) {
- //HACK: Using a ContentView here, so that it creates a native View object
- const anchor = createAndAttach('template', ContentView, parentElement);
- anchor.visibility = "collapse";
- anchor.templateParent = parentElement;
- return anchor;
-}
+ if (lowered.indexOf(ANDROID_PREFX) === 0) {
+ if (this.isAndroid) {
+ return attribute.substr(ANDROID_PREFX.length);
+ } else {
+ return null;
+ }
+ }
-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;
+ return attribute;
}
-}
-export function setProperty(view: NgView, attributeName: string, value: any): void {
- if (attributeName.indexOf(".") !== -1) {
- // Handle nested properties
- const properties = attributeName.split(".");
- attributeName = properties[properties.length - 1];
-
- let propMap = getProperties(view);
- let i = 0;
- while (i < properties.length - 1 && isDefined(view)) {
- var prop = properties[i];
- if (propMap.has(prop)) {
- prop = propMap.get(prop);
- }
+ public setProperty(view: NgView, attributeName: string, value: any): void {
+ attributeName = this.platformFilter(attributeName);
+ if (!attributeName) {
+ return;
+ }
- view = view[prop];
- propMap = getProperties(view);
- i++;
+ if (attributeName.indexOf(".") !== -1) {
+ // Handle nested properties
+ const properties = attributeName.split(".");
+ attributeName = properties[properties.length - 1];
+
+ let propMap = this.getProperties(view);
+ let i = 0;
+ while (i < properties.length - 1 && isDefined(view)) {
+ var prop = properties[i];
+ if (propMap.has(prop)) {
+ prop = propMap.get(prop);
+ }
+
+ view = view[prop];
+ propMap = this.getProperties(view);
+ i++;
+ }
}
- }
- if (isDefined(view)) {
- setPropertyInternal(view, attributeName, value);
+ if (isDefined(view)) {
+ this.setPropertyInternal(view, attributeName, value);
+ }
}
-}
-function setPropertyInternal(view: NgView, attributeName: string, value: any): void {
- traceLog('Setting attribute: ' + attributeName);
-
- let specialSetter = getSpecialPropertySetter(attributeName);
- let propMap = getProperties(view);
-
- if (attributeName === "class") {
- setClasses(view, 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] = convertValue(value);
- } else {
- // Unknown attribute value -- just set it to our object as is.
- view[attributeName] = convertValue(value);
+ private setPropertyInternal(view: NgView, attributeName: string, value: any): void {
+ traceLog('Setting attribute: ' + attributeName);
+
+ let specialSetter = getSpecialPropertySetter(attributeName);
+ let propMap = this.getProperties(view);
+
+ if (attributeName === "class") {
+ this.setClasses(view, value);
+ } else if (this.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] = this.convertValue(value);
+ } else {
+ // Unknown attribute value -- just set it to our object as is.
+ view[attributeName] = this.convertValue(value);
+ }
}
-}
-function convertValue(value: any): any {
- if (typeof (value) !== "string" || value === "") {
- return value;
- }
+ private convertValue(value: any): any {
+ if (typeof (value) !== "string" || value === "") {
+ return value;
+ }
- var valueAsNumber = +value;
- if (!isNaN(valueAsNumber)) {
- return valueAsNumber;
- } else if (value && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
- return value.toLowerCase() === "true" ? true : false;
- } else {
- return value;
+ var valueAsNumber = +value;
+ if (!isNaN(valueAsNumber)) {
+ return valueAsNumber;
+ } else if (value && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
+ return value.toLowerCase() === "true" ? true : false;
+ } else {
+ return value;
+ }
}
-}
-const propertyMaps: Map> = new Map>();
+ private getProperties(instance: any): Map {
+ let type = instance && instance.constructor;
+ if (!type) {
+ return 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);
}
- if (!propertyMaps.has(type)) {
- var propMap = new Map();
- for (var propName in instance) {
- propMap.set(propName.toLowerCase(), propName);
+ private cssClasses(view: NgView) {
+ if (!view.cssClasses) {
+ view.cssClasses = new Map();
}
- propertyMaps.set(type, propMap);
+ return view.cssClasses;
}
- return propertyMaps.get(type);
-}
-
-function cssClasses(view: NgView) {
- if (!view.cssClasses) {
- view.cssClasses = new Map();
+ public addClass(view: NgView, className: string): void {
+ this.cssClasses(view).set(className, true);
+ this.syncClasses(view);
}
- 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);
-}
+ public removeClass(view: NgView, className: string): void {
+ this.cssClasses(view).delete(className);
+ this.syncClasses(view);
+ }
-function syncClasses(view: NgView): void {
- let classValue = (Array).from(cssClasses(view).keys()).join(' ');
- view.cssClass = classValue;
-}
+ private setClasses(view: NgView, classesValue: string): void {
+ let classes = classesValue.split(whiteSpaceSplitter)
+ classes.forEach((className) => this.cssClasses(view).set(className, true));
+ this.syncClasses(view);
+ }
+ private syncClasses(view: NgView): void {
+ let classValue = (Array).from(this.cssClasses(view).keys()).join(' ');
+ view.cssClass = classValue;
+ }
-export function setStyleProperty(view: NgView, styleName: string, styleValue: string): void {
- throw new Error("Not implemented: setStyleProperty");
+ public setStyleProperty(view: NgView, styleName: string, styleValue: string): void {
+ throw new Error("Not implemented: setStyleProperty");
+ }
}
diff --git a/tests/app/tests/platform-filter-components.ts b/tests/app/tests/platform-filter-components.ts
new file mode 100644
index 000000000..55507aa5d
--- /dev/null
+++ b/tests/app/tests/platform-filter-components.ts
@@ -0,0 +1,95 @@
+//make sure you import mocha-config before angular2/core
+import {assert} from "./test-config";
+import {Component, ElementRef, provide} from "angular2/core";
+import {ProxyViewContainer} from "ui/proxy-view-container";
+import {dumpView, createDevice} from "./test-utils";
+import {TestApp} from "./test-app";
+import {DEVICE} from "../nativescript-angular/platform-providers";
+import {platformNames} from "platform";
+
+@Component({
+ template: `
+
+
+ `
+})
+export class IosSpecificComponent {
+ constructor(public elementRef: ElementRef) { }
+}
+
+@Component({
+ template: `
+
+
+ `
+})
+export class AndroidSpecificComponent {
+ constructor(public elementRef: ElementRef) { }
+}
+
+
+describe('Platofrm filter components', () => {
+ describe('on IOS device', () => {
+ let testApp: TestApp = null;
+
+ before(() => {
+ return TestApp.create([provide(DEVICE, { useValue: createDevice(platformNames.ios) })]).then((app) => {
+ testApp = app;
+ })
+ });
+
+ after(() => {
+ testApp.dispose();
+ });
+
+ it("does render ios sepecific conternt", () => {
+ return testApp.loadComponent(IosSpecificComponent).then((componentRef) => {
+ const componentRoot = componentRef.instance.elementRef.nativeElement;
+ assert.equal(
+ "(ProxyViewContainer (StackLayout (ProxyViewContainer (template), (Label[text=IOS]))))",
+ dumpView(componentRoot, true));
+ });
+ });
+
+ it("does not render android sepecific conternt", () => {
+ return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => {
+ const componentRoot = componentRef.instance.elementRef.nativeElement;
+ assert.equal(
+ "(ProxyViewContainer (StackLayout (ProxyViewContainer (template))))",
+ dumpView(componentRoot, true));
+ });
+ });
+ })
+
+ describe('on Android device', () => {
+ let testApp: TestApp = null;
+
+ before(() => {
+ return TestApp.create([provide(DEVICE, { useValue: createDevice(platformNames.android) })]).then((app) => {
+ testApp = app;
+ })
+ });
+
+ after(() => {
+ testApp.dispose();
+ });
+
+ it("does render android sepecific conternt", () => {
+ return testApp.loadComponent(AndroidSpecificComponent).then((componentRef) => {
+ const componentRoot = componentRef.instance.elementRef.nativeElement;
+ assert.equal(
+ "(ProxyViewContainer (StackLayout (ProxyViewContainer (template), (Label[text=ANDROID]))))",
+ dumpView(componentRoot, true));
+ });
+ });
+
+ it("does not render ios sepecific conternt", () => {
+ return testApp.loadComponent(IosSpecificComponent).then((componentRef) => {
+ const componentRoot = componentRef.instance.elementRef.nativeElement;
+ assert.equal(
+ "(ProxyViewContainer (StackLayout (ProxyViewContainer (template))))",
+ dumpView(componentRoot, true));
+ });
+ });
+ })
+})
diff --git a/tests/app/tests/property-sets.ts b/tests/app/tests/property-sets.ts
index eabd63641..50ad95536 100644
--- a/tests/app/tests/property-sets.ts
+++ b/tests/app/tests/property-sets.ts
@@ -3,9 +3,11 @@ import {assert} from "./test-config";
import {bootstrap} from "../nativescript-angular/application";
import {Component} from "angular2/core";
import {View} from "ui/core/view";
-import {setProperty} from "../nativescript-angular/view-util";
+import {ViewUtil} from "../nativescript-angular/view-util";
import {NgView, ViewExtensions, ViewClassMeta} from "../nativescript-angular/element-registry";
import {Red} from "color/known-colors";
+import {device, Device, platformNames} from "platform";
+import {createDevice} from "./test-utils";
class TestView extends View implements ViewExtensions {
public nodeName: string = "TestView";
@@ -20,60 +22,95 @@ class TestView extends View implements ViewExtensions {
public nested: { property: string } = { property: "untouched" };
}
+const iosDevice = createDevice(platformNames.ios);
+const androidDevice = createDevice(platformNames.android);
+
describe('setting View properties', () => {
+ var viewUtil: ViewUtil;
+ before(() => {
+ viewUtil = new ViewUtil(device);
+ })
+
it('preserves string values', () => {
let view = new TestView();
- setProperty(view, "stringValue", "blah")
+ viewUtil.setProperty(view, "stringValue", "blah")
assert.equal("blah", view.stringValue);
});
it('converts number values', () => {
let view = new TestView();
- setProperty(view, "numValue", "42")
+ viewUtil.setProperty(view, "numValue", "42")
assert.strictEqual(42, view.numValue);
- setProperty(view, "numValue", 0)
+ viewUtil.setProperty(view, "numValue", 0)
assert.strictEqual(0, view.numValue);
});
it('converts boolean values', () => {
let view = new TestView();
- setProperty(view, "boolValue", "true")
+ viewUtil.setProperty(view, "boolValue", "true")
assert.strictEqual(true, view.boolValue);
- setProperty(view, "boolValue", "false")
+ viewUtil.setProperty(view, "boolValue", "false")
assert.strictEqual(false, view.boolValue);
});
it('sets style values', () => {
let view = new TestView();
- setProperty(view, "style", "color: red")
+ viewUtil.setProperty(view, "style", "color: red")
assert.equal(Red, view.style.color.hex);
});
it('doesn\'t convert blank strings', () => {
let view = new TestView();
- setProperty(view, "stringValue", "")
+ viewUtil.setProperty(view, "stringValue", "")
assert.strictEqual("", view.stringValue);
});
it('doesn\'t convert booleans', () => {
let view = new TestView();
- setProperty(view, "boolValue", true)
+ viewUtil.setProperty(view, "boolValue", true)
assert.strictEqual(true, view.boolValue);
- setProperty(view, "boolValue", false)
+ viewUtil.setProperty(view, "boolValue", false)
assert.strictEqual(false, view.boolValue);
});
it('preserves objects', () => {
let value = { name: "Jim", age: 23 };
let view = new TestView();
- setProperty(view, "anyValue", value)
+ viewUtil.setProperty(view, "anyValue", value)
assert.deepEqual(value, view.anyValue);
});
it('sets nested properties', () => {
let view = new TestView();
- setProperty(view, "nested.property", "blah")
+ viewUtil.setProperty(view, "nested.property", "blah")
assert.strictEqual("blah", view.nested.property);
});
-
+
+ it('sets ios property in ios', () => {
+ let view = new TestView();
+ let testUtil = new ViewUtil(iosDevice);
+ testUtil.setProperty(view, "@ios:anyValue", "blah")
+ assert.strictEqual("blah", view.anyValue);
+ });
+
+ it('doesn\'t set android property in ios', () => {
+ let view = new TestView();
+ let testUtil = new ViewUtil(iosDevice);
+ testUtil.setProperty(view, "@android:anyValue", "blah")
+ assert.isUndefined(view.anyValue);
+ });
+
+ it('sets android property in android', () => {
+ let view = new TestView();
+ let testUtil = new ViewUtil(androidDevice);
+ testUtil.setProperty(view, "@android:anyValue", "blah")
+ assert.strictEqual("blah", view.anyValue);
+ });
+
+ it('doesn\'t set ios property in android', () => {
+ let view = new TestView();
+ let testUtil = new ViewUtil(androidDevice);
+ testUtil.setProperty(view, "@ios:anyValue", "blah")
+ assert.isUndefined(view.anyValue);
+ });
});
diff --git a/tests/app/tests/test-app.ts b/tests/app/tests/test-app.ts
index 1ebabae6b..8a31c1080 100644
--- a/tests/app/tests/test-app.ts
+++ b/tests/app/tests/test-app.ts
@@ -38,8 +38,8 @@ export class TestApp {
}
}
- public static create(): Promise {
- return bootstrapTestApp(TestApp);
+ public static create(providers?: ProviderArray): Promise {
+ return bootstrapTestApp(TestApp, providers);
}
public dispose() {
diff --git a/tests/app/tests/test-utils.ts b/tests/app/tests/test-utils.ts
index 2426c82f4..b051f4988 100644
--- a/tests/app/tests/test-utils.ts
+++ b/tests/app/tests/test-utils.ts
@@ -1,5 +1,6 @@
import {View} from "ui/core/view";
import {TextBase} from "ui/text-base";
+import {Device} from "platform";
function getChildren(view: View): Array {
var children: Array = [];
@@ -27,3 +28,17 @@ export function dumpView(view: View, verbose: boolean = false): string {
output.push(")");
return output.join("");
}
+
+export function createDevice(os: string): Device {
+ return {
+ os: os,
+ osVersion: "0",
+ deviceType: "phone",
+ language: "en",
+ uuid: "0000",
+ sdkVersion: "0",
+ region: "US",
+ manufacturer: "tester",
+ model: "test device"
+ }
+}
\ No newline at end of file
diff --git a/tests/hooks/after-prepare/nativescript-unit-test-runner.js b/tests/hooks/after-prepare/nativescript-unit-test-runner.js
index 45ca49f8a..47df3458a 100644
--- a/tests/hooks/after-prepare/nativescript-unit-test-runner.js
+++ b/tests/hooks/after-prepare/nativescript-unit-test-runner.js
@@ -1 +1 @@
-module.exports = require("nativescript-unit-test-runner/lib/after-prepare");
+module.exports = require("nativescript-unit-test-runner/lib/after-prepare");
diff --git a/tests/package.json b/tests/package.json
index c3666271f..2d2c54354 100644
--- a/tests/package.json
+++ b/tests/package.json
@@ -30,7 +30,7 @@
},
"homepage": "http://nativescript.org",
"dependencies": {
- "tns-core-modules": "2.0.0-angular-1",
+ "tns-core-modules": "2.0.0-angular-2",
"nativescript-intl": "^0.0.2",
"angular2": "2.0.0-beta.9",
"nativescript-unit-test-runner": "^0.3.3",