Erstellen Sie Widgets in Mendix mit React Teil 2 - Timer
Der Mendix Die Plattform ermöglicht es Entwicklern, ihre Apps mit React über das Pluggable Widgets-Framework zu erweitern. Mit diesem Framework können Entwickler leistungsstarke Bibliotheken nutzen und die Benutzeroberfläche erweitern, indem sie React-Komponenten erstellen, die dann in der Mendix Studio Pro.
In diesem Blog werden wir Erfahren Sie mehr über Typescript und ich werde Ihnen zeigen, wie Sie verwenden Sie Attribute aus Ihrem Mendix Modell in Ihrem Widget, Wie man Nutzen Sie die Browser-API und wir werden sogar bedingtes Rendern in React.
Dies ist der zweite Blog einer mehrteiligen Serie, Blog 1 finden Sie hier: Erstellen Sie Widgets in Mendix mit React — Teil 1 — Farbzähler
Wann Sie Pluggable Widgets verwenden sollten
Bei der Entscheidung, ob ein neues Pluggable Widget erstellt werden soll, verwende ich einige Prüfkriterien:
- Braucht es eine UI-Komponente? — wenn Sie Javascript verwenden müssen, aber keine UI-Komponente vorhanden ist, JS-Aktion könnte für Ihren Anwendungsfall besser geeignet sein
- Gibt es sie im Mendix Marketplace?
- Verwendet es die Browser-API oder ein NPM-Paket?
Was wir bauen
Dieses Mal werden wir bauen ein Timer zum Aktualisieren einer Seite in einem festen Intervall. „Warum mache ich das nicht einfach mit JavaScript?“, höre ich Sie fragen. Das liegt daran, dass wir auch eine visuelle Komponente haben werden, die gibt an, wie lange es bis zur nächsten Aktualisierung dauert.

Dies basierte auf einem realen Anwendungsfall, bei dem ein Dashboard in einer Werkstatt angezeigt wurde und alle 30 Sekunden aktualisiert werden musste

Erste Schritte
Für dieses Widget verwenden wir Typoskript, der stark typisierte Wrapper um Javascript, da er zwar das Schreiben von Code etwas schwieriger macht, aber eingebaute Flusen , umfangreiche Dokumentation sind sehr nützlich beim Erstellen von Pluggable Widgets. Diese Elemente von Typescript machen es auch viel einfachere Zusammenarbeit in großen Teams , Integration mit komplexen APIs.

Wir beginnen mit dem Aufbau unseres Widgets, wie in 1 Blog aber wählen Sie dieses Mal Typescript als unsere Programmiersprache.
Eine kurze Anmerkung zu Funktionskomponenten vs. Klassenkomponenten in React. In diesen Blogs verwenden wir funktionale KomponentenSie können jedoch auch stoßen Sie auf React, das mit Klassenkomponenten geschrieben wurde. Das ist weil, , bis die Lösen von Haken in React 16.8, Klassenkomponenten waren die einzige Möglichkeit, den Status zu verwalten, und funktionale Komponenten waren hauptsächlich eine Präsentationsschicht für das Frontend. Jetzt ist die React-Community Übergang zur Verwendung funktionaler Komponenten wie sie sind syntaktisch einfacher und vieles mehr dekorativen.
Entwicklungszeit
Eine Erklärung der Pluggable Widget-Ordnerstruktur und wie Sie Ihre Entwicklungsumgebung einrichten, finden Sie in meinem letzten Blog.
Eines vorweg
Um den Entwicklungsprozess zu beschleunigen, werden wir unsere Editor-Vorschaudatei entfernen. Diese Datei erstellt die Vorschau für unser Widget im Designmodus in Studio Pro oder Studio, und darum kümmern wir uns in diesem Handbuch nicht. Es wäre nur eine weitere Sache, die aktualisiert werden müsste. Also entfernen wir die Datei: Timer.editorPreview.tsx.
Die Kurz
Wir können das, was wir mit unserem Widget erreichen möchten, in drei Hauptabschnitte unterteilen:
- Schritt 1: Stellen Sie den Timer auf einen Anfangswert ein, der definiert ist in Mendix
- Schritt 2: Verringern Sie den Timer in 1-Sekunden-Schritten
- Schritt 3: Ausführen einer Aktion
Schritt 1: Stellen Sie den Timer auf einen Anfangswert ein, der definiert ist in Mendix
Beginnen wir mit der Erstellung der Schnittstelle zwischen unserem Mendix Modell und Widget. Wir könnten eine einfache Ganzzahl verwenden, aber durch die Verwendung eines Attributs haben wir mehr Flexibilität, um den Timerwert zur Laufzeit dynamisch zu ändern.
Wir aktualisieren unsere Timer.xml, sodass sie Folgendes enthält:
<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>
Wenn wir schon dabei sind, sollten wir auch die untergeordnete Komponente in TimeText und die Schnittstelle in TimeTextProps umbenennen. Wenn wir die Datei speichern und den Befehl ausführen:npm run build, wir werden Fehler sehen, das ist gut!

Es zeigt, dass die Eigenschaft sampleText durch timeAtt ersetzt wurde. Aktualisieren wir unsere Timer-Datei mit timeAtt anstelle des standardmäßigen sampleText.
Wenn wir den timeAtt in unserer Timer.tsx-Datei markieren, können wir sehen, dass er vom Typ ist EditableValue<Big>.
BearbeitbarerWert ist das generischer Typ zur Abwicklung, Integrierung, Speicherung und Mendix verwendet, um alle Attribute darzustellen, und es ermöglicht uns, die Werte innerhalb der Mendix Modell.
Die<>' Teil, wird durch den Attributtyp bestimmt. Für ganzzahlige oder dezimale Attribute verwenden wir die Big.js-Bibliothek. Damit wird sichergestellt, dass die in Ihrer Anwendung verwendeten Zahlen nicht durch standardmäßige JavaScript-Zahlenbeschränkungen eingeschränkt werden.
Status verwenden
Bisher haben wir eine Verbindung hergestellt zwischen Mendix und das Widget über die Props der Container-Komponente. Jetzt müssen wir einen leicht lesbaren Wert an unsere TimeText-Komponente weitergeben, um ihn auf der Seite anzuzeigen. Dazu verarbeiten wir unsere EditableValue<Big> und speichern Sie eine einfache Ganzzahl in der Zustand unserer Containerkomponente.
Wir verwenden Zustand hier statt Hilfsmittel weil wir unseren Timerwert im Laufe der Zeit ändern werden und Requisiten sollten niemals direkt geändert werden:
Beginnen wir mit dem Importieren useState von 'React' und Initialisierung eines Zustand namens Zeit mit den folgenden:
const [time, setTime] = useState<number>();
Damit erhalten wir eine Zustandsvariable (Zeit) und eine setState-Funktion zum Ändern dieses Status (wie bei Props darf der Status niemals direkt geändert werden, sondern kann über diese Funktion geändert werden).
Verwenden von useEffect
Als nächstes müssen wir Übergeben Sie unseren Attributwert an unseren Status. Der einfachste Weg, dies zu tun, besteht darin, einen Wert an unseren useState zu übergeben:
const [time, setTime] = useState<number>(timeAtt.value.toNumber());
Da unser Wert aber leer sein könnte, werden wir das nicht tun. Stattdessen verwenden wir ein weiteres zentrales Konzept von React: „useEffect“.
Was ist also useEffect? Im Wesentlichen führt es nach jedem Rendern eine Funktion aus. Wir können es verwenden, um unsere Zeit wie folgt einzustellen:
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"}/>;
}
Wir können dies dann an die untergeordnete Komponente TimeText weitergeben, indem wir die Eigenschaften aktualisieren, damit sie unseren neuen Zeitstatus akzeptieren:
return <TimeText value={time}/>;
Und das ist unsere aktualisierte Timer-Komponente!
Jetzt müssen wir nur noch ein paar kleine Änderungen an unserer TimeText-Komponente vornehmen, um Akzeptieren Sie die neuen Requisiten. Da wir Typescript verwenden, haben wir eine Schnittstelle für unsere Komponente. Dies definiert, wie jeder Code, der diese Komponente verwendet, mit ihr interagieren kann. Aktualisieren wir also unsere Anzeigekomponente wie folgt:
export interface TimeTextProps {
value: number | undefined;
}
export function TimeText({ value }: TimeTextProps): ReactElement {
return <div>{value} seconds</div>;
}
Schnittstellen scheinen hier unnötig zu sein, und für ein kleines Widget wie dieses sind sie das wahrscheinlich auch, aber für wenn Projekte größer werden, sind sie unglaublich nützlich.
Zum Testen können wir öffnen Mendix Studio Pro und drücken Sie F4, um die Aktualisierungen mit unserem Widget zu synchronisieren. Anschließend müssen wir unsere Seite so einrichten, dass ein Zahlenattribut an das Timer-Widget übergeben werden kann.

We Klicken Sie auf Ausführen und… auf unserer Seite steht nur „Sekunden“.

Mach dir keine Sorgen Dies ist zu erwarten, da Mendix lädt Attribute. Der Attributwerte werden asynchron geladen, das macht den Mendix Front-End so schnell, bedeutet aber, dass wir ein paar zusätzliche Codezeilen benötigen, damit unser Widget funktioniert.
UseEffect verwenden, aber richtig
Den zweiten Parameter von useEffect haben wir leer gelassen, Dies bedeutet, dass die Funktion useEffect einmal ausgeführt wird, wenn die Komponente zum ersten Mal bereitgestellt wird., an welchem Punkt unser Attributwert ist nicht bereit.
Wenn Sie dies überprüfen möchten, setzen Sie eine console.log(`Render ${timeAtt.value}`) über der if-Anweisung und überprüfen Sie die Browserkonsole („Strg + Umschalt + i“ im Browser).
Stattdessen möchten wir, dass die Funktion useEffect immer ausgeführt wird, wenn timeAtt aktualisiert wird. Dazu fügen wir timeAtt einfach zum zweiten Parameter von useEffect hinzu.
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
return <TimeText value={time}/>;
}
Wenn Sie die Anwendung erneut ausführen von Mendix Ihr Timerwert wird angezeigt. Wenn Sie das Konsolenprotokoll überprüfen, sehen Sie, dass das Widget zweimal gerendert wurde: einmal, als der Wert nicht definiert war, und einmal mit der Zahl.


ERFOLGREICH! Teil 1 ist erledigt!
Schritt 2: Verringern Sie den Timer in 1-Sekunden-Schritten
Cool. Wir haben also eine Zahl auf dem Bildschirm angezeigt, aber das nützt nicht viel, Wir möchten, dass unser Timer herunterzählt. Dazu werden wir die Javascript-API des Browsers nutzen, speziell window.setTimeout(), Die führt eine Funktion nach einer festgelegten Zeitspanne aus.
Indem wir dies mit unserem alten Freund useEffect kombinieren, können wir einen Countdown erstellen. Wenn wir unsere useEffect-Funktion von unserem Zeitstatus abhängig machen und 1 Sekunde warten, um unseren Zeit Zustand um 1 Sekunde, erstellen wir im Wesentlichen eine Schleife.
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}/>;
}
Da dieser Zählvorgang jedoch für immer weiterläuft, müssen wir eine weitere Bedingung hinzufügen, um ihn zu stoppen.
useEffect(() => {
if (time !== undefined) {
if (time > 0) {
window.setTimeout(() => setTime(time - 1), 1000);
}
}
}, [time]);
Wenn wir unser Widget neu laden, können wir sehen, dass wir unseren Countdown haben.

Eine Erinnerung: Wenn Sie npm run start und achten Sie auf Änderungen an Ihrem Widget Sie müssen den Cache leeren und aktualisieren (Öffnen Sie in Chrome die Browsertools und klicken Sie mit der rechten Maustaste auf die Schaltfläche „Aktualisieren“)
Schritt 3: Eine Aktion ausführen
Jetzt muss etwas passieren, wenn der Countdown 0 erreicht!
Glücklicherweise Mendix bietet eine sehr einfach zu verwendende API zur Integration mit dem Modell und zur Ausführung einer Aktion. Zuerst aktualisieren wir die Datei Timer.xml, sodass sie Folgendes enthält:
<property key="action" type="action" required="true">
<caption>Action</caption>
<description>Action to trigger when the time elapses</description>
</property>
Wir können dann eine Funktion hinzufügen, um die Aktion in unserem Timer.tsx auszuführen, und unsere useEffect-Funktion aktualisieren, um die Aktion bei 0 auszuführen.
Unsere endgültige Timer.tsx sollte folgendermaßen aussehen:
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} />;
}
Wir können diesen neuen Code in unserem Mendix App, indem Sie F4 drücken und eine auszuführende Aktion auswählen.

Indem wir einen Mikroflow aufrufen, der die Seite aktualisiert, erhalten wir unseren Wiederholungstimer.
Eine letzte Sache
Beim ersten Laden unserer Komponente gab es einen kurzen Zeitraum, in dem der an die TimeText-Komponente übergebene Zeitwert nicht definiert war. Dies ist kein großes Problem, da es nur dazu führt, dass das Widget „Sekunden“ rendert, aber es ist bewährte Vorgehensweise, einen Hinweis darauf zu geben, dass der Wert geladen wird.
Dazu verwenden wir ein gängiges Muster in React für bedingtes Rendern.
Mit einer if-Anweisung können wir prüfen, ob der Wert noch nicht definiert ist und das Laden wie folgt anzeigen:
export function TimeText({ value }: TimeTextProps): ReactElement {
if (value === undefined) {
return <div>Loading</div>;
} else {
return <div>{value}</div>;
}
}
Das ist aber sehr wortreich. Wir verwenden ternäre Operatoren aus ES6 um es abzukürzen auf:
export function TimeText({ value }: TimeTextProps): ReactElement {
return value ? <div>{value} seconds</div> : <div>Loading</div>;}
Und VOILA!

Zusammenfassung
Herzlichen Glückwunsch, Sie haben ein Pluggable Widget erstellt!
In diesem Build haben wir die Pluggable Widget API behandelt, wie Sie Daten von Ihrem Mendix Modell und ändern Sie es; behandelt die Verwendung von Typescript, um besser zu verstehen, wie die Integration mit unserem Mendix App, habe wichtige React-Konzepte noch einmal durchgesehen und gelernt, wie man Aktionen ausführt Mendix Modell in einem Widget.
Den Endzustand dieses Widgets finden Sie hier: GitHub – joe-robertson-mx/timer
Als nächstes erstellen wir ein Kanban-Board mit Mendix und Pluggable Widgets, um mehr darüber zu erfahren, wie Widgets kombiniert werden können, um elegante Endbenutzererlebnisse zu schaffen, und wie Sie Änderungen vornehmen können, die in Ihrem Mendix Datenbank.