在这篇博客中,我们将 了解 Typescript 我将向你展示如何 使用您的属性 Mendix 您的小部件中的模型、如何 利用浏览器 API 我们甚至会谈到 React 中的条件渲染.
这是系列博文中的第二篇,第一篇博文可在此处找到: 在以下位置构建小部件 Mendix 使用 React — 第一部分 — 颜色计数器
何时使用可插入小部件
在决定是否创建新的可插入小部件时,我会使用一些试金石:
- 它需要 UI 组件吗?——如果你需要使用 Javascript,但没有 UI 组件, JS 动作 可能更适合你的用例
- 是否可在 Mendix 应用市场?
- 它是否使用浏览器 API 或 NPM 包?
我们正在建设什么
这次我们要建造 以固定间隔刷新页面的计时器“我为什么不直接用 JavaScript 来实现呢?”我听到你问。那是因为我们还将有一个可视化组件, 告诉您下次刷新还有多长时间.

这是基于一个真实用例,其中仪表板显示在车间,需要每 30 秒更新一次

新会员入门指南
对于这个小部件,我们将使用 打字稿,JavaScript 的强类型包装器,尽管它最初使代码编写起来稍微困难一些, 内置 linting 和 详尽的文件 在创建可插入小部件时非常有用。Typescript 的这些元素也使其更加 更容易在大型团队中协作 和 与复杂 API 集成.

我们首先搭建我们的小部件,如下所示 博客1 但这次选择 Typescript 作为我们的编程语言。
关于 React 中的功能组件与类组件的简要说明。 在这些博客中,我们将使用功能组件但是,你也可以 遇到使用类组件编写的 React。 这是因为, 直到 释放钩子 在 React 16.8 中, 类组件是管理状态的唯一方法而函数式组件主要是前端的展示层。现在 React 社区 转向使用功能组件 像他们那样 语法上更简单 更多 轻巧.
开发时间
有关可插入式小部件文件夹结构的说明以及如何设置开发环境,请参阅我的 东南亚数字经济博客.
首先
为了加快开发过程,我们将删除编辑器预览文件。此文件在 Studio Pro 或 Studio 中的设计模式下为我们的小部件创建预览,对于本指南,我们不必担心这一点。这只是需要更新的一件事。所以让我们继续删除文件: Timer.editorPreview.tsx。
简要
我们可以将我们试图通过小部件实现的功能分为 3 个主要部分:
- 第三步: 将计时器设置为在中定义的初始值 Mendix
- 第三步: 以 1 秒为间隔减少计时器
- 第三步: 执行操作
步骤 1:将计时器设置为 Mendix
让我们首先创建我们的 Mendix 模型和小部件。我们可以使用简单的整数,但通过使用属性,我们可以更灵活地在运行时动态更改计时器值。
我们更新 Timer.xml 以包含:
<property key="timeAtt" type="attribute" required="true
<caption>Time attribute</caption>
<description>The number of seconds on refresh</description>
<attributeTypes>
<attributeType name="Integer"/>
</attributeTypes>
</property>
在此过程中,我们还应该将子组件重命名为 TimeText,并将接口重命名为 TimeTextProps。保存文件并运行命令:npm run build,我们会看到错误,这很好!

它显示 sampleText 属性已被 timeAtt 替换。让我们用 timeAtt 替换默认的 sampleText 来更新我们的 Timer 文件。
如果我们在 Timer.tsx 文件中突出显示 timeAtt,我们可以看到它是以下类型: EditableValue<Big>.
可编辑值 是 泛型 这 Mendix 用于表示所有属性,它允许我们读取和写入 Mendix 模型。
左侧工具栏上的'<>' 部分,由属性类型决定。对于整数或小数属性,我们使用 Big.js 库。这是为了确保您的应用程序中使用的数字不受默认 JavaScript 数字限制的约束。
使用状态
到目前为止,我们已经在以下两个之间创建了链接: Mendix 以及小部件,通过容器组件的 props。现在我们需要将一个易于阅读的值传递给我们的 TimeText 组件,以显示在页面上。为此,我们将处理我们的 EditableValue<Big> 并将一个简单的整数存储在 州 我们的容器组件。
我们使用 州 这里而不是 道具 因为我们会随着时间推移改变计时器的值, props 不应该直接改变:
让我们从导入开始 使用状态 来自“React”并初始化一个 州 被称为 次 具有以下内容:
const [time, setTime] = useState<number>();
这给了我们一个状态变量(次) 和一个 setState 函数来改变该状态(与 props 一样,状态绝不能直接改变,而是可以通过此函数进行改变)。
使用 useEffect
接下来,我们需要 将我们的属性值传递到我们的状态中最简单的方法是将一个值传递到我们的 useState 中:
const [time, setTime] = useState<number>(timeAtt.value.toNumber());
但是因为我们的值可能为空,所以我们不会这么做。相反,我们将使用 React 的另一个核心概念“useEffect”。
那么 useEffect 是什么?本质上,它在每次渲染后执行一个函数。我们可以用它来设置时间,如下所示:
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, []);
return <TimeText sampleText={sampleText ? sampleText:"World"}/>;
}
然后我们可以通过更新 props 来接受新的时间状态,并将其传递给子组件 TimeText:
return <TimeText value={time}/>;
这就是我们的 Timer 组件更新完毕了!
现在我们只需要对我们的 TimeText 组件做一些小的修改就可以了 接受新道具因为我们使用的是 Typescript,所以我们有一个 接口 对于我们的组件。这定义了使用此组件的任何代码如何与其交互。因此,让我们将显示组件更新为:
export interface TimeTextProps {
value: number | undefined;
}
export function TimeText({ value }: TimeTextProps): ReactElement {
return <div>{value} seconds</div>;
}
在这里,接口可能看起来是不必要的,对于像这样的小部件来说,它们可能是不必要的,但是对于 当项目变大时它们非常有用.
为了测试,我们可以打开 Mendix Studio Pro 并按 F4 将更新同步到或小部件。然后我们需要设置页面,以便将数字属性传递给计时器小部件。

We 点击运行 并且……我们的页面只显示‘秒’。

别担心 这是预料之中的,因为 Mendix 加载属性。 该 属性值异步加载,这就是 Mendix 前端如此之快,但这意味着我们需要额外的几行代码才能使我们的小部件正常工作。
使用 useEffect 但要正确
我们将 useEffect 的第二个参数留空, 这意味着 useEffect 函数在组件首次挂载时运行一次,此时 我们的属性值尚未准备好.
如果你想检查这一点,请输入 console.log(`Render ${timeAtt.value}`) 在 if 语句上方并检查浏览器控制台(在浏览器中按“Ctrl + Shift + i”)。
因此,我们希望每当 timeAtt 更新时运行 useEffect 函数,为此我们只需将 timeAtt 添加到 useEffect 的第二个参数。
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
return <TimeText value={time}/>;
}
如果您从 Mendix 您将看到您的计时器值出现,如果您检查控制台日志,您将看到小部件渲染了两次,一次是值未定义时,一次是数字。


成功!第一部分完成了!
步骤 2:以 1 秒为间隔减少计时器
太棒了。虽然屏幕上显示了数字,但用处不大, 我们希望我们的计时器倒计时.为此,我们将 使用浏览器 Javascript API, 特别是 window.setTimeout(),这 在固定时间后执行一个函数.
通过将其与我们的老朋友 useEffect 结合起来,我们可以创建一个倒计时。如果我们让 useEffect 函数依赖于我们的时间状态,并等待 1 秒钟来改变我们的 次 状态持续 1 秒,我们本质上创建了一个循环。
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
>seEffect(() => {
if (time !== undefined) {
window.setTimeout(() => setTime(time - 1), 1000);
}
}, [time]);
return <TimeText value={time}/>;
}
然而这个计数会一直持续下去,所以我们需要添加一个条件来让它停止。
useEffect(() => {
if (time !== undefined) {
if (time > 0) {
window.setTimeout(() => setTime(time - 1), 1000);
}
}
}, [time]);
如果我们重新加载我们的小部件,我们可以看到我们有倒计时。

提醒一下,如果你使用 npm run start 并观察你的小部件上的变化 您需要清除缓存并刷新 (在 Chrome 中打开浏览器工具并右键单击刷新按钮)
步骤 3:执行操作
现在我们需要在倒计时到达 0 时发生一些事情!
幸 Mendix 提供了一个非常易于使用的 API 来与模型集成并执行操作。首先,我们更新 Timer.xml 以包含:
<property key="action" type="action" required="true">
<caption>Action</caption>
<description>Action to trigger when the time elapses</description>
</property>
然后,我们可以添加一个函数来在我们的 Timer.tsx 上执行操作,并更新我们的 useEffect 函数以在 0 时执行操作。
我们的最终 Timer.tsx 应该如下所示:
export function Timer({ timeAtt, action }: TimerContainerProps): ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (time !== undefined) {
if (time > 0) {
window.setTimeout(() => setTime(time - 1), 1000);
} else {
execAction();
}
}
}, [time]);
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
const execAction = () => {
if (action && action.canExecute) {
action.execute();
}
};
return <TimeText value={time} />;
}
我们可以在我们的 Mendix 通过按 F4 并选择要执行的操作来应用程序。

通过选择调用刷新页面的微流,我们得到了重复计时器。
最后一件事
当我们的组件首次加载时,有一段短暂的时间传递给 TimeText 组件的时间值未定义。这不是一个大问题,因为它只会导致小部件呈现“秒”,但最佳做法是给出一些指示,表明该值正在加载。
为此,我们将使用 React 中的常见模式进行条件渲染。
我们可以使用 if 语句来检查该值是否尚未定义并显示加载,如下所示:
export function TimeText({ value }: TimeTextProps): ReactElement {
if (value === undefined) {
return <div>Loading</div>;
} else {
return <div>{value}</div>;
}
}
但这太冗长了。 我们可以使用 ES6 中的三元运算符 将其缩短为:
export function TimeText({ value }: TimeTextProps): ReactElement {
return value ? <div>{value} seconds</div> : <div>Loading</div>;}
瞧!

结语
恭喜,您构建了一个可插入小部件!
在此版本中,我们介绍了可插入小部件 API,如何从您的 Mendix 模型并改变它;使用 Typescript 进行介绍,以便更好地理解如何与我们的 Mendix 应用程序,重新审视关键的 React 概念,并学习了如何从你的 Mendix 小部件中的模型。
该小部件的最终状态可以在这里找到: GitHub – joe-robertson-mx/timer
接下来我们将使用 Mendix 和可插入小部件,以了解有关如何组合小部件以创建流畅的最终用户体验的更多信息,以及如何进行持久的更改 Mendix 数据库。