Merge pull request #403 from NativeScript/custom-component-load

Custom component loading improved + tests
This commit is contained in:
Vladimir Enchev
2015-07-08 09:08:42 +03:00
3 changed files with 111 additions and 47 deletions

View File

@ -40,6 +40,43 @@ export function test_load_ShouldNotCrashWithoutExports() {
TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";"); TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";");
}; };
export function test_loadWithOptionsNoXML() {
var v = builder.load({
path: "~/xml-declaration/mymodule",
name: "MyControl",
exports: exports
});
TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";");
};
export function test_loadWithOptionsWithXML() {
var v = builder.load({
path: "~/xml-declaration/mymodulewithxml",
name: "MyControl",
exports: exports
});
TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";");
};
export function test_loadWithOptionsFromTNS() {
var v = builder.load({
path: "ui/label",
name: "Label"
});
TKUnit.assert(v instanceof labelModule.Label, "Expected result: Label; Actual result: " + v + ";");
};
export function test_loadWithOptionsFromTNSPath() {
var v = builder.load({
path: "tns_modules/ui/label",
name: "Label"
});
TKUnit.assert(v instanceof labelModule.Label, "Expected result: Label; Actual result: " + v + ";");
};
export function test_parse_ShouldNotCrashWithoutExports() { export function test_parse_ShouldNotCrashWithoutExports() {
var fileAccess = new fileSystemAccess.FileSystemAccess(); var fileAccess = new fileSystemAccess.FileSystemAccess();
@ -159,7 +196,7 @@ export function test_parse_ThrowErrorWhenNestingPlatforms() {
} catch (ex) { } catch (ex) {
e = ex; e = ex;
} }
TKUnit.assert(e, "Expected result: Error; Actual result: " + e); TKUnit.assert(e, "Expected result: Error; Actual result: " + e);
}; };

View File

@ -3,5 +3,12 @@ declare module "ui/builder" {
import view = require("ui/core/view"); import view = require("ui/core/view");
export function load(fileName: string, exports?: any): view.View; export function load(fileName: string, exports?: any): view.View;
export function load(options: LoadOptions): view.View;
export function parse(value: string, exports?: any): view.View; export function parse(value: string, exports?: any): view.View;
export interface LoadOptions {
path: string;
name: string;
exports?: any;
}
} }

View File

@ -6,6 +6,7 @@ import types = require("utils/types");
import componentBuilder = require("ui/builder/component-builder"); import componentBuilder = require("ui/builder/component-builder");
import templateBuilderDef = require("ui/builder/template-builder"); import templateBuilderDef = require("ui/builder/template-builder");
import platform = require("platform"); import platform = require("platform");
import definition = require("ui/builder");
var KNOWNCOLLECTIONS = "knownCollections"; var KNOWNCOLLECTIONS = "knownCollections";
@ -18,14 +19,14 @@ function isCurentPlatform(value: string): boolean {
return value && value.toLowerCase() === platform.device.os.toLowerCase(); return value && value.toLowerCase() === platform.device.os.toLowerCase();
} }
export function parse(value: string, exports: any): view.View { export function parse(value: string, context: any): view.View {
var viewToReturn: view.View; var viewToReturn: view.View;
if (exports instanceof view.View) { if (context instanceof view.View) {
exports = getExports(exports); context = getExports(context);
} }
var componentModule = parseInternal(value, exports); var componentModule = parseInternal(value, context);
if (componentModule) { if (componentModule) {
viewToReturn = componentModule.component; viewToReturn = componentModule.component;
@ -34,7 +35,7 @@ export function parse(value: string, exports: any): view.View {
return viewToReturn; return viewToReturn;
} }
function parseInternal(value: string, exports: any): componentBuilder.ComponentModule { function parseInternal(value: string, context: any): componentBuilder.ComponentModule {
var rootComponentModule: componentBuilder.ComponentModule; var rootComponentModule: componentBuilder.ComponentModule;
// Temporary collection used for parent scope. // Temporary collection used for parent scope.
var parents = new Array<componentBuilder.ComponentModule>(); var parents = new Array<componentBuilder.ComponentModule>();
@ -112,40 +113,12 @@ function parseInternal(value: string, exports: any): componentBuilder.ComponentM
var componentModule: componentBuilder.ComponentModule; var componentModule: componentBuilder.ComponentModule;
if (args.prefix) { if (args.prefix && args.namespace) {
// Custom components // Custom components
componentModule = loadCustomComponent(args.namespace, args.elementName, args.attributes, context);
var ns = args.namespace;
if (ns) {
var xmlPath = fs.path.join(fs.knownFolders.currentApp().path, ns, args.elementName) + ".xml";
if (fs.File.exists(xmlPath)) {
// Custom components with XML
var jsPath = xmlPath.replace(".xml", ".js");
var subExports;
if (fs.File.exists(jsPath)) {
// Custom components with XML and code
subExports = require(jsPath.replace(".js", ""))
}
componentModule = loadInternal(xmlPath, subExports);
// Attributes will be transfered to the custom component
if (types.isDefined(componentModule) && types.isDefined(componentModule.component)) {
var attr: string;
for (attr in args.attributes) {
componentBuilder.setPropertyValue(componentModule.component, subExports, exports, attr, args.attributes[attr]);
}
}
} else {
// Custom components without XML
componentModule = componentBuilder.getComponentModule(args.elementName, ns, args.attributes, exports);
}
}
} else { } else {
// Default components // Default components
componentModule = componentBuilder.getComponentModule(args.elementName, ns, args.attributes, exports); componentModule = componentBuilder.getComponentModule(args.elementName, args.namespace, args.attributes, context);
} }
if (componentModule) { if (componentModule) {
@ -190,7 +163,7 @@ function parseInternal(value: string, exports: any): componentBuilder.ComponentM
} }
} }
},(e) => { }, (e) => {
throw new Error("XML parse error: " + e.message); throw new Error("XML parse error: " + e.message);
}, true); }, true);
@ -202,9 +175,56 @@ function parseInternal(value: string, exports: any): componentBuilder.ComponentM
return rootComponentModule; return rootComponentModule;
} }
export function load(fileName: string, exports: any): view.View { function loadCustomComponent(componentPath: string, componentName?: string, attributes?: Object, context?: Object): componentBuilder.ComponentModule {
var result: componentBuilder.ComponentModule;
componentPath = componentPath.replace("~/", "");
var fileName = componentPath;
if (!fs.File.exists(fileName)) {
fileName = fs.path.join(fs.knownFolders.currentApp().path, componentPath, componentName) + ".xml";
}
if (fs.File.exists(fileName)) {
// Custom components with XML
var jsPath = fileName.replace(".xml", ".js");
var subExports;
if (fs.File.exists(jsPath)) {
// Custom components with XML and code
subExports = require(jsPath.replace(".js", ""))
}
result = loadInternal(fileName, subExports);
// Attributes will be transfered to the custom component
if (types.isDefined(result) && types.isDefined(result.component) && types.isDefined(attributes)) {
var attr: string;
for (attr in attributes) {
componentBuilder.setPropertyValue(result.component, subExports, context, attr, attributes[attr]);
}
}
} else {
// Custom components without XML
result = componentBuilder.getComponentModule(componentName, componentPath, attributes, context);
}
return result;
}
export function load(arg: any): view.View {
var viewToReturn: view.View; var viewToReturn: view.View;
var componentModule = loadInternal(fileName, exports); var componentModule: componentBuilder.ComponentModule;
if (arguments.length === 1) {
if (!types.isString(arguments[0])) {
var options = <definition.LoadOptions>arguments[0];
componentModule = loadCustomComponent(options.path, options.name, undefined, options.exports);
} else {
componentModule = loadInternal(<string>arguments[0]);
}
} else {
componentModule = loadInternal(<string>arguments[0], arguments[1]);
}
if (componentModule) { if (componentModule) {
viewToReturn = componentModule.component; viewToReturn = componentModule.component;
@ -213,25 +233,25 @@ export function load(fileName: string, exports: any): view.View {
return viewToReturn; return viewToReturn;
} }
function loadInternal(fileName: string, exports: any): componentBuilder.ComponentModule { function loadInternal(fileName: string, context?: any): componentBuilder.ComponentModule {
var componentModule: componentBuilder.ComponentModule; var componentModule: componentBuilder.ComponentModule;
// Check if the XML file exists. // Check if the XML file exists.
if (fileName && fs.File.exists(fileName)) { if (fs.File.exists(fileName)) {
var fileAccess = new file_access_module.FileSystemAccess(); var fileAccess = new file_access_module.FileSystemAccess();
// Read the XML file. // Read the XML file.
fileAccess.readText(fileName, result => { fileAccess.readText(fileName, result => {
componentModule = parseInternal(result, exports); componentModule = parseInternal(result, context);
},(e) => { }, (e) => {
throw new Error("Error loading file " + fileName + " :" + e.message); throw new Error("Error loading file " + fileName + " :" + e.message);
}); });
} }
if (componentModule && componentModule.component) { if (componentModule && componentModule.component) {
// Save exports to root component (will be used for templates). // Save exports to root component (will be used for templates).
(<any>componentModule.component).exports = exports; (<any>componentModule.component).exports = context;
} }
return componentModule; return componentModule;
@ -252,8 +272,8 @@ function getComplexProperty(fullName: string): string {
return name; return name;
} }
function isKnownCollection(name: string, exports: any): boolean { function isKnownCollection(name: string, context: any): boolean {
return KNOWNCOLLECTIONS in exports && exports[KNOWNCOLLECTIONS] && name in exports[KNOWNCOLLECTIONS]; return KNOWNCOLLECTIONS in context && context[KNOWNCOLLECTIONS] && name in context[KNOWNCOLLECTIONS];
} }
function addToComplexProperty(parent: componentBuilder.ComponentModule, complexProperty, elementModule: componentBuilder.ComponentModule) { function addToComplexProperty(parent: componentBuilder.ComponentModule, complexProperty, elementModule: componentBuilder.ComponentModule) {