JavaScript Action可以通过几个新功能扩展应用程序。要最有效地实现JavaScript Action,请务必遵循以下最佳实践。
此最佳实践将教您如何执行以下 Action:
JavaScript Action在浏览器中运行,每个浏览器版本都有自己的JavaScript标准样式实现。因此,某些 Action可以在某些浏览器中运行,但不能在其他浏览器中运行。为了兼容性,建议使用ECMAScript 5。
较旧的浏览器可能没有实现新的ES6功能,因此从internet复制和粘贴现代示例代码时要小心,尤其是在必须支持IE11的情况下。一些ECMAScript 6函数由Mendix 客户端渲染填充。
Mendix Studio Pro包含来自Core JS的以下polyfills:
Mendix Studio Pro还包含一个用于Mozilla的fetchapi的polyfill
创建JavaScript Action时,可以使用输入参数。您的JavaScript Action将被其他人使用,但您永远不知道它们是否会被正确使用。要使 Action更健壮,请验证所有输入参数,并在可能时启用默认值。
使用以下代码验证输入字符串文本:
/**
* @param {string} text
*/
async function TextToSpeech(text) {
// BEGIN USER CODE
if (text === undefined) {
// Throw an error when the parameter is set to 'empty', the value will be undefined
throw new Error("The Text parameter is required");
}
if (text.trim() === "") {
// Throw an error when the text is an empty string ""
throw new Error("The Text parameter can not be empty");
}
/* implementation */
// END USER CODE
}
使用以下代码验证Mendix输入对象:
/**
* @param {MxObject} audioFile
*/
async function PlayAudio(audioFile) {
// BEGIN USER CODE
if (!audioFile) {
throw new Error("The 'Audio file' parameter can not be empty");
}
if (!audioFile.isA("System.FileDocument") && !audioFile.inheritsFrom("System.FileDocument")) {
throw new Error("The 'Audio file' parameter should inherit from System.FileDocument");
}
if (!audioFile.get("HasContents")) {
throw new Error("The 'Audio file' parameter does not have any content");
}
const allowedExtensions = ["mp3", "wav", "ogg"]
const fileName = audioFile.get("Name");
const dotIndex = fileName.lastIndexOf(".");
const extension = fileName.substring(dotIndex + 1).toLowerCase();
if (dotIndex === -1 || allowedExtensions.indexOf(extension) === -1) {
throw new Error("The 'Audio file' parameter only supports files with extension .mp3, .wav or .ogg");
}
/* implementation */
// END USER CODE
}
使用以下代码验证对象和属性名称的输入列表:
/**
* @param {MxObject[]} objectList
* @param {string} attributeName
* @returns {Promise.}
*/
async function SumListAttributeValues(objectList, attributeName) {
// BEGIN USER CODE
if (!attributeName || attributeName.trim() === "") {
throw new Error("The 'Attribute name' parameter can not be empty");
}
if (!objectList || objectList.length === 0) {
// Return early, sum of empty is 0
return new Big(0);
}
if (!objectList[0].has(attributeName)) {
throw new Error("List of type " + objectList[0].getEntity() + " does not have an attribute named " + attributeName);
}
if (!objectList[0].isNumeric(attributeName)) {
throw new Error("List of type " + objectList[0].getEntity() + " an attribute named " + attributeName + " is not numeric");
}
/* implementation */
// END USER CODE
}
将此代码用于默认输入值:
/**
* @param {Big} targetSize
* @param {"Module.PictureSource.camera"|"Module.PictureSource.gallery"} pictureSource
* @param {boolean} correctOrientation
* @param {string} waterMark
*/
function CameraStart(targetSize, pictureSource, correctOrientation, waterMark) {
// BEGIN USER CODE
targetSize = targetSize && targetSize > 0 ? targetSize : 150; // numeric
pictureSource = pictureSource ? pictureSource : "camera"; // enumeration
correctOrientation = correctOrientation ? true : false; // boolean
waterMark = waterMark !== undefined ? waterMark : "DEMO"; // string
/* implementation */
// END USER CODE
}
要自定义JavaScript Action,请参阅下面的部分。
在JavaScript Action中,提供了完整的Mendix客户端API。如需参考,请参阅Mendix客户端API。请注意,Mendix客户端API的某些部分是为小部件创建的,与JavaScript Action关系不大。
当您使用decimal、integer或long类型的参数时,您的参数将不像您在JavaScript中使用的那样是数字。相反,它将是一个Big对象,它来自一个名为big.js由Mendix客户使用。这是为了确保应用程序中使用的数字不受默认JavaScript数字限制的约束。
// Precision limitation of JavaScript numbers
0.1 + 0.2 // 0.30000000000000004
// Solved with BigJs
x = new Big(0.1)
y = x.plus(0.2) // '0.3'
如果您知道您的JavaScript Action不需要这种扩展精度(例如,如果您期望一个1到100之间的简单整数),那么您可以轻松地将一个big对象转换为JavaScript数字:
const numberValue = Number(bigJsValue); // number
了解如何使用big.js,咨询big.js应用程序编程接口。
使用以下代码创建对象:
mx.data.create({
entity: "MyFirstModule.Cat",
callback: function(object) {
console.log("Object created on server");
},
error: function(error) {
console.error("Could not commit object:", error);
}
});
有关创建对象的更多信息,请参阅Mendix客户端API的创建部分。
使用以下代码更改对象:
mxobj.get("Name"); // "Fred"
mxobj.set("Name", "Henry");
mxobj.get("Name"); // "Henry"
mxobj.getOriginalValue("Name") // "Fred"
使用以下代码加载平台附带的依赖项(请注意,附带的依赖项可能因Mendix版本而异):
// Synchronous libs that are already loaded
var lang = require("mendix/lang");
Mendix客户提供以下库:
虽然有Dojo和Document Object Model(DOM)函数可用,但不建议使用它们。有关Dojo和DOM函数的更多信息,请参阅本文下面的“理解错误实践”部分。
当前不支持加载和绑定外部库。在JavaScript中嵌入库代码和CSS并不理想。将库JavaScript文件和CSS添加到主题文件夹中,并在索引.html以及组件.json建议使用。
下面是使用基于pdf库的外部依赖关系的示例:
默认情况下,Mendix hybrid App附带了大量插件。
也可以在移动构建期间添加新插件。
实际使用的插件列表可以在部署包中的config.xml中找到。
JavaScript Action可以指定返回类型,例如Integer、DateTime、Object、Object列表和泛型。
JavaScript Action可以是同步的,也可以是异步的。同步 Action将直接返回值并完成执行。异步 Action将返回一个promise,并将在稍后继续执行和完成。当promise得到解决时,Nano Flow将继续执行。
JavaScript的核心是一种同步编程语言,它一次运行一行代码。如果一行代码正在执行,它会阻止Mendix客户端中的所有其他JavaScript运行,从而使Mendix客户端看起来很慢。异步函数解决了这个问题。对于异步函数,当结果可用时,存储函数以供以后执行。这样,就不会阻止其他JavaScript运行。
当结果直接可用时,请使用以下代码使用同步返回:
/**
* @param {Big} valueA
* @param {Big} valueB
* @param {Big}
*/
function AddValue(valueA, valueB) {
// BEGIN USER CODE
return valueA.plus(valueB)
// END USER CODE
}
使用以下代码在nanoflow需要等待 Action完成时使用异步返回:
function Wait(delay) {
// BEGIN USER CODE
return new Promise(function(resolve) {
window.setTimeout(function(){
resolve();
}, delay);
});
// END USER CODE
}
许多api和函数是以异步方式设计的,并且使用回调函数或promise。JavaScript Action要求返回一个promise。Promise应该按照行动中预期的返回值来解决。
Promise对象表示异步 Action的最终完成(或失败)及其结果值。
使用以下代码将回调API包装到promise中:
function AskConfirmation(question) {
// BEGIN USER CODE
return new Promise(function (resolve) {
mx.ui.confirmation({
content: question,
handler: function() {
resolve(true);
},
onCancel: function() {
resolve(false);
}
});
});
// END USER CODE
}
解释回调代码:
此函数使用Fetch API:
async function GetUserNameSampleRest(userID) {
// BEGIN USER CODE
if (!userID) {
throw new Error("The UserID parameter is required")
}
const url = "https://jsonplaceholder.typicode.com/users/" + userID;
try {
const response = await fetch(url); // Fetch returns a promise, gets the url and wait for result
const jsonData = await response.json(); // Transform to json
return jsonData.name; // Get the data
} catch (error) {
throw new Error("Failed to get user information");
}
// END USER CODE
}
解释获取API代码:
以下是最常用的promise函数:
JavaScript语言最近增加了异步函数和await关键字。这些特性使异步代码看起来更像同步代码,从而使异步代码更易于编写和读取。在代码中使用async/await有两个部分:
对于错误处理,有两个选项:*使用带有async/await的同步try…catch结构,并将异步函数调用包装到其中。catch(error){}块将接收到rejected Promise*的error对象,该对象将.catch(error)块链接到.then()调用的末尾
2.3.3.1 使用Promise函数最佳实践
使用promise函数时,请注意以下几点:
要最有效地创建和优化JavaScript Action,请参阅下面的小节。
有了设计良好的api,JavaScript Action将更易于重用。设计API时请考虑以下准则:
考虑以下关于最佳API实践的附加技术建议:
JavaScript Acton可以在带有JavaScript Action调用活动的nanoflow中使用。也可以公开活动列表中的 Action。这将使开发人员更容易找到 Action。建议仅公开将经常重用的 Action。
使用类别对动作进行分组,并使用图标使暴露的nanoflow动作在nanoflow中易于识别:
在App Explorer中右键单击JavaScript Action,然后选择export document to file,可以导出单个 Action。然后,导出的文件可以与其他开发人员共享。单个Nano Flow不能在Mendix市场上发布。相反,将其作为模块发布。
您可以通过右键单击应用程序资源管理器中的模块,然后选择“从文件导入文档”来导入单个 Action。接下来,选择JavaScript Action文件。
单个Nano Flow行动不能在Mendix市场上公布。您可以将一个作为模块发布,但建议将相关的nanoflow Action作为模块内的一个组发布。对于包含多个nanoflow Action的模块,使用相关数据模型(如“实体”)对 Action进行分组,并为外部依赖关系提供相关文档。将模块作为一个整体导出并上传到Mendix Marketplace。
文档化的 Action更容易重用。记录时请考虑以下事项:
一个广泛的测试应用程序可以帮助使JavaScript Action更加健壮。在测试应用程序中,尝试创建输入的所有可能变化,考虑应该处理的空输入和错误情况。
测试时,请确保检查所有兼容平台(web、混合和本机)。web应该处理Mendix浏览器兼容性。有关兼容性的更多信息,请参阅系统需求的浏览器部分。
当 Action与平台不兼容时,请确保在遇到错误之前可以使用其他 Action检查该 Action。例如,在启动摄影机之前,使用CheckCameraSupport Action。当调用某个 Action但不兼容时,它应该优雅地失败或显示清晰的错误消息。
Mendix Studio Pro不支持nanoflow调试,其方式与支持微流调试的方式相同。要验证 Action之间的中间结果,请使用日志消息活动。
调试JavaScript Action的代码可以在浏览器开发工具中完成。有关如何做到这一点的详细信息,请参阅浏览器的开发工具文档,网址为ChromeDevTools、Firefox developer tools、Microsoft Edge developer tools或Safari的Web开发工具。
最初,JavaScript Action的源代码未加载。它们的源代码将在模块 Action的第一次执行之前加载。从那一刻起,如果您使用Chrome,您可以在文件夹javascript Action中的Developer Tools的Sources选项卡中找到源代码。
加载文件后,可以通过单击内联编号(A,在下面的截图)在代码中设置断点。或者,选择暂停捕获的异常可以用来查找问题(B)。最后,您可以通过添加行调试器来更改源代码;(C)。此语句将在第一次执行 Action时启动调试工具,并将应用断点:
并非所有功能都建议使用。考虑 Action可能对Mendix客户端、DOM或其他小部件产生的副作用:
不要假定用户的浏览器–记住并非所有浏览器都具有相同的功能
永久性渲染应该使用可插入的小部件来完成——新的Mendix客户端将随意呈现页面并删除您的更改(例如,当您呈现DOM时,index.html))
由于Mendix客户端可以随意呈现DOM,因此可能会丢失对DOM的更改(例如,当您将CSS类添加到另一个组件时,Mendix客户端将随意呈现页面并删除您的更改)-您可以创建和更改放置在外部的DOM元素
避免使用不推荐的库–不要使用Dojo或Dijit,因为它们将被弃用(jQuery也不应该再使用)
避免使用返回undefined的布尔 Action–布尔变量是唯一需要值的变量,是唯一可接受的状态为true或false(其他变量可以设置为undefined,并且可以在Mendix Studio Pro中作为$variable != empty)
更多信息,请访问以下链接:
Mendix官网:https://www.mendix.com/zh/
Mendix行业解决方案:https://solutions.mendix.com/
Mendix平台指南:https://www.mendix.com/evaluation-guide/
Mendix动画展示:https://www.mendix.com/demos/
Mendix公众号