Erstellen Sie Widgets in Mendix mit React Teil 3 - Kanban
Mendix ist die Low-Code-Plattform Nummer eins, mit der Hersteller Anwendungen deutlich schneller entwickeln können als mit herkömmlichem Code. Die Mendix Das Frontend wird mit React erstellt und Entwickler können ihre Anwendungen mit Pluggable Widgets erweitern.
In diesem Blog erfahren Sie, wie Sie von Ihrem Widget aus dauerhafte Datenänderungen in Ihrer Anwendung vornehmen, Widgets kombinieren, um elegante Endbenutzererlebnisse, benutzerdefinierte Hooks und mehr zu erstellen …
Dies ist der dritte Blog einer mehrteiligen Serie, Blogs 1 und 2 finden Sie hier: Erstellen Sie Widgets in Mendix mit React — Teil 1 — Farbzähler und Erstellen Sie Widgets in Mendix mit React Teil 2 – Timer.
Bevor wir beginnen, ein kleiner Haftungsausschluss: Für diesen Build nutzen wir einige der Funktionen von VS Code. Die Anweisungen können abweichen, wenn Sie eine andere IDE bevorzugen.
Was wir bauen
Mendix hat vor kurzem veröffentlicht Epen, ein neues, voll ausgestattetes Story-Management-Tool!
Dazu gehört auch eine wirklich nette Komponente, mit der man seine Geschichten per Drag & Drop verschieben kann. Ich dachte, es wäre cool, zu versuchen, etwas Ähnliches zu bauen.

Es gibt ein paar Dinge, die jedes Kanban-Board können sollte:
- Ermöglichen Sie Benutzern das Festlegen der Kanban-Phasen
- Ändern Sie die Phase eines Elements durch Ziehen
- Die Sortierreihenfolge von Elementen in einem Abschnitt kann durch Ziehen geändert werden.
Erste Schritte
Zu Beginn erstellen wir das Widget, indem wir den Widget-Generator ausführen, um schnell ein Widget zu erstellen. Führen Sie den Befehl aus:
yo @mendix/widget kanban
Wählen Sie beim Konfigurieren der Widget-Eigenschaften die Standardoptionen aus und achten Sie dabei darauf, Verwenden Sie Typescript.
Als nächstes aktualisieren wir die Schnittstelle zwischen unserem Widget und dem Mendix durch Aktualisierung von Kanban.xml. Dazu fügen wir eine Liste von Datenquellenelementen hinzu, damit der Benutzer des Widgets angeben kann, was Mendix Entitäten, die sie verwenden möchten.
Wir haben zwei Möglichkeiten, die Elemente im Widget darzustellen. Wir können traditionelles JSX verwenden oder dem Benutzer erlauben, das gewünschte Design innerhalb von MendixDie zweite Option hat drei Hauptvorteile:
- Verhindert die Vorgabe der Struktur, in der die untergeordneten Daten angezeigt werden müssen
- Versteckt keinen Code in schwieriger zu bearbeitenden kompilierten Widget-Dateien
- Ermöglicht Benutzern die Nutzung des WYSIWG-Editors innerhalb Mendix
Rendern der Liste
Option zwei gewinnt! Um dies umzusetzen, können wir die 'widgets'-Tag aus dem Pluggable Widgets APIDie aktualisierte Kanban.xml enthält jetzt:
Artikel Liste der Gegenstände Inhalt Widgets, die Datenquellen verwenden
Als Nächstes können wir unsere Kanban.TSX-Datei aktualisieren, sodass sie Folgendes enthält:
Exportfunktion Kanban({ Widgetliste, Elemente }: KanbanContainerProps): ReactElement { Return ( {items.items && items.items.map(item => { return widgetList.get(item);})} ); }
Im obigen Code passieren verschiedene Dinge, deshalb werde ich sie im Folgenden aufschlüsseln:
- „Widgetliste“ist unsere Widget-Schnittstelle in die Mendix Modell: Wenn wir mit der rechten Maustaste auf „widgetList“ klicken und auf „Zur Typdefinition gehen“ klicken, können wir unsere Dokumentation für diesen Typ anzeigen.
Export deklarieren Schnittstelle ListWidgetValue {
* Gibt vom Benutzer konfigurierte Widgets in Form eines Reaktionsknotens zurück, der basierend auf dem bereitgestellten Objektelement gerendert wird.*
* @param item Instanz von {@link ObjectItem} aus der verknüpften Datenquelle.*/
erhalten: (Element: ObjectItem) => ReactNode;
}
Dies sagt uns im Wesentlichen, dass es eine Get-Funktion für unser Widget gibt, die etwas namens ObjectItem erwartet, um einen ReactNode zu rendern
- „Items“ ist unsere Datenquelle, auf die wir als ListValue zugreifen können (ein Mendix-definierter Typ), wenn wir mit der rechten Maustaste auf „Elemente“ klicken und auf „Zur Typdefinition gehen“ klicken, können wir unsere Dokumentation für diesen Typ sehen. Aus dieser Dokumentation erkenne ich, dass ich auf ein Array von ObjectItem zugreifen kann, indem ich den .Artikel Resorts
Wir können dann die integrierte Funktion JS-Map verwenden, um jedes Element in der Artikelliste zu durchlaufen und die „Get“-Funktion des Artikels verwenden, um das Ergebnis zurückzugeben.
- Wir haben dann Umhüllen Sie unsere Funktion in Geschweifte Klammern um anzuzeigen, dass es sich um JavaScript handelt und es in ein übergeordnetes Div um jedes Element zu rendern
- Zuletzt fügen wir ein ternärer Ausdruck um den Aufruf der Funktion zu vermeiden, falls die Liste keine Elemente enthält.
Tests
Um unsere Implementierung zu testen, haben wir ein Testprojekt im Ordner „Tests“ unseres Widgets eingerichtet, genau wie in 1 Blog.
Wir fügen eine Entität namens Artikel mit dem String-Attribut Inhalt

Dann fügen wir einige Mikroflüsse hinzu Erstellen Sie einige $Items im After Start Up Microflow.

Wir können dann unser Widget vom Terminal aus erstellen mit npm run build .
Wenn wir zurückkehren zu unserem Mendix Modell und drücken Sie F4. Schon steht unser Widget zur Platzierung auf unserer Homepage bereit.

Hier habe ich einen einfachen Container mit der Klasse 'Karte' und eine Zeichenfolge, die den Inhalt anzeigt. Und Voila! Es wird gerendert.

Ziehen und Ablegen
Eine statische Liste ist ein guter Anfang, aber wir müssen die Elemente verschieben können!
Wählen wir unsere React Drag & Drop-Bibliothek aus. Wir könnten diese selbst erstellen (und einige kluge Mendix Entwickler haben das bereits getan!), aber es gibt eine sehr breite Auswahl an gut unterstützten, häufig genutzten Bibliotheken.
Ich bin gegangen mit reagieren-schön-dnd da es eine einfach zu bedienende API und ist sehr funktionsreich.
Anschließend importieren wir unsere Drag & Drop-Bibliothek, indem wir zu unserem Widget-Verzeichnis (./kanban) navigieren und den Befehl ausführen
npm i react-beautiful-dnd .
Aus der Dokumentation geht hervor, dass wir für unser Widget drei Hauptbereiche benötigen:

<DragDropContext />- Umschließt den Teil Ihrer Anwendung, für den Sie Drag & Drop aktivieren möchten<Droppable />- Ein Bereich, in den man hineinfallen kann.<Draggable />- Was kann man mitschleppen
Schritt 1 – Ziehbar
Beginnen wir also auf der untersten Ebene und konvertieren unsere Elemente so, dass sie gezogen werden können.
Lassen Sie uns zunächst unsere ItemList in eine separate Funktion aufteilen, sie aber zur einfacheren Entwicklung vorerst in unserer Komponente belassen (wir können sie später in eine separate Komponente verschieben).
Exportfunktion Kanban({ Widgetliste, Elemente }: KanbanContainerProps): ReactElement {
const ItemList = (): ReactElement => {
zurückkehren ( {items.items && items.items.map(item => { return widgetList.get(item);})} );
}
zurückkehren ( ) }
Um jedes Listenelement verschiebbar zu machen, müssen wir das Widget in eine Draggable-Komponente einbinden, die erwartet eine eindeutige ID für das verschiebbare Element — einen Index zur Sortierung der Artikel, und eine untergeordnete Funktion zum Rendern der Komponente.
Wenn wir für den Rest der Implementierung der Dokumentation der Bibliothek react-beautiful-dnd folgen, erhalten wir den folgenden Code.
const ItemList = (): ReactElement => { return ( {items.items &&items.items.map((item, i) => { return ( {vorausgesetzt => ( {widgetList.get(Element)} )} ); })} ); };
Was wir hier getan haben, ist, unsere Widget-Komponente in einen „ziehbaren“ Wrapper einzuschließen, der es uns ermöglicht, eine Referenz und Eigenschaften weiterzugeben.
Referenzen sind ein Konzept, das wir in diesen Blogs noch nicht behandelt haben und das ein wichtiger Teil von React ist. Sie sind im Wesentlichen eine Möglichkeit, auf Elemente innerhalb Ihrer Seite zuzugreifen und sind in diesem Fall dazu gedacht, den Überblick darüber zu behalten, was gezogen wird: Refs und das DOM – React.
Schritt 2 – Abwerfbar
Da wir nun unsere Draggable-Komponenten haben, müssen wir die Drag-and-Drop-Leinwand erstellen.
Dazu müssen wir unsere ItemList mit Draggable-Komponenten in einen Dropgable-Container einbinden. Dabei verwenden wir ein Muster, das dem oben beschriebenen sehr ähnlich ist:
{vorausgesetzt => ( )}
Schritt 3 – Kontext
Zuletzt müssen wir den Drag-and-Drop-Kontext bereitstellen, in dem die Funktionalität existiert:
{vorausgesetzt => ( )}
Der Kontext erwartet, dass eine Funktion aufgerufen wird, sobald ein Element gezogen wurde. Lassen Sie uns zunächst einfach protokollieren, dass die Funktion aufgerufen wurde.
Unser Code sollte derzeit ungefähr so aussehen:
Exportfunktion Kanban({ Widgetliste, Elemente }: KanbanContainerProps): ReactElement { const ItemList = (): ReactElement => { return ( {items.items && items.items.map((item, i) => { return ( {vorausgesetzt => ( {widgetList.get(Element)} )} ); })} ); };
Funktion onDragEnd(Ergebnis: DropResult): void { console.log("Gezogen"); }
zurückkehren ( {vorausgesetzt => ( )} ); }
Wenn wir laufen npm run build und wiederholen Sie unsere Mendix App können wir jetzt unsere Karten herumziehen! Irgendwie…

Wir werden später noch einmal auf dieses Problem zurückkommen. Das Wichtigste ist, dass wir unsere Elemente verschieben können.
Mehrere Spalten
Wir können also Dinge in einer Liste nach oben und unten ziehen, aber was wir wirklich wollen, ist, Elemente zwischen Spalten ziehen zu können, um den Status zu bestimmen.
Wir möchten, dass unsere Benutzer des Widgets angeben können, wie viele Spalten sie möchten.
Dazu können wir verwenden 'Objekte' in unserer Kanban.xml. Wir müssen nur unsere vorhandenen Eigenschaften in eine Objektliste einbinden, wie folgt:
Abschnitte Name Name Abschnittselemente Liste der Gegenstände Inhalt Widgets, die Datenquellen verwenden
Die Aufmerksamen unter Ihnen werden auch bemerken, dass ich einen Namen hinzugefügt habe, damit wir den Überblick über unsere Spalten behalten.
Wenn Sie öffnen Mendix Wir können uns unsere schöne neue Benutzeroberfläche ansehen:

Cool, aber jetzt müssen wir unsere Spalten rendern. Gehen wir zurück zu unserem Widget …
Das ist eigentlich ganz einfach.
Aktualisieren wir zunächst unser JSX, um für jedes Objekt eine Spalte zu rendern:
{meinObjekt.map((obj, i) => { return ( {vorausgesetzt => ( )} ); })}
Wir sollten auch die Ablagefähige ID einzigartig und übergebe einige Requisiten an unsere ItemList.
{meinObjekt.map((obj, i) => { return ( {vorausgesetzt => ( )} ); })}
Dies bedeutet, dass wir auch unsere Artikelliste aktualisieren müssen, um die neuen Eigenschaften zu akzeptieren. Und wenn wir schon dabei sind, können wir sie auch gleich in etwas umgestalten, das leichter zu lesen ist. Am Ende habe ich zwei neue Dateien in meinem Komponentenordner:
Ziehbar.TSX
importiere { ReactElement, createElement } von „react“; importiere { Draggable } von „react-beautiful-dnd“; importiere { ObjectItem, ListWidgetValue } von „mendix“;
Exportschnittstelle DraggableProps { Element: ObjectItem; i: Zahl; WidgetList: ListWidgetValue; }
Exportfunktion DraggableItem({ item, i, widgetList }: DraggableProps): ReactElement { return ( {vorausgesetzt => ( {widgetList.get(Element)} )} ); }
Und ItemList.TSX
importiere { ReactElement, createElement } von „react“; importiere { ListValue, ListWidgetValue } von „mendix“; importiere { DraggableItem } von „./Draggable“;
Exportschnittstelle ItemListProps { Elemente: ListValue; WidgetList: ListWidgetValue; }
export const ItemList = ({ Elemente, Widgetliste }: ItemListProps): ReactElement => { return ( Elemente && ( {Artikel.Artikel && Artikel.Artikel.map((Artikel, i) => { return ; })} ) ); };
Styling
Wir müssen unserem Kanban-Board etwas Stil verleihen, um es von einem hässlichen Entlein in einen wunderschönen Schwan zu verwandeln.
Wir werden dies im Wesentlichen auf zwei Arten tun:
- Durch die Nutzung von CSS-Klassen und die 'className'-Eigenschaft jedes Dom-Element hat
- Nutzung von Inline-Styles, um in unserem Code berechnete Eigenschaften (z. B. Spaltenbreite) zu nutzen
Aktualisieren wir zunächst unsere Kanban.css-Datei auf
.kanban-col { Rand: 15px; Polsterung: 10px; Hintergrund: #9bedff; Rahmenradius: 15px; }
.kanban-col h6 { text-align: center; }
Wir können dann einige zusätzliche Dom-Elemente, unsere Klassen und Stile verwenden und erhalten am Ende Folgendes:
importiere { ItemList } aus "./components/ItemList"; importiere { KanbanContainerProps } aus "../typings/KanbanProps";
importiere "./ui/Kanban.css";
Exportfunktion Kanban({ meinObjekt }: KanbanContainerProps): ReactElement {
Funktion onDragEnd(Ergebnis: DropResult): void { console.log("gezogen"); }
zurückkehren ( {meinObjekt.map((obj, i) => { return ( {vorausgesetzt => ( {obj.Abschnittsname} {bereitgestellt.Platzhalter} )} ); })} ); }
Moment mal… was ist das? ${100/myObject.length}%
This is a Vorlagenliteral Dadurch können Sie schnell Zeichenfolgen in JS erstellen, indem Sie Zeichenfolgen in „ schreiben und Ausdrücke in ${} umschließen.
Aber zurück zum Widget … wir haben jetzt benutzerdefinierte Spalten mit Elementen.

Drag & Drop-Aktion
Jetzt möchten wir, dass unser Drag & Drop tatsächlich etwas bewirkt …

Dazu verwenden wir die Pluggable Widget Action API in Kombination mit einer Datenquelle.
Als erstes fügen wir unserem Objekt in unserer Kanban.xml zwei neue Eigenschaften hinzu
Abschnittselemente Liste der Gegenstände Alle Artikel Liste der Gegenstände
Indem wir unsere Aktion mit einer Datenquelle verknüpfen, können wir die Aktion mit einem Parameter ausführen aus Mendix. Dadurch können wir die Attribute unserer Artikel ändern.
Jetzt können wir unsere „onDragEnd ”Funktion zum Ausführen der Aktion.
Wenn wir die Dokumentation für react-beautiful-dnd lesen, können wir sehen, dass onDragEnd ein Ergebnisobjekt erhält, das uns Zugriff auf die draggableId und droppableID ermöglicht. Wir können diese verwenden, um herauszufinden, welches Element gezogen wird und wohin es gezogen wird (und daher, welche Aktion wir aufrufen müssen).
Als Erstes müssen wir unser Ergebnis destrukturieren und das richtige Objekt finden:
const { Quelle, Ziel } = Ergebnis; const destObj = meinObjekt[parseInt(Ziel.droppableId, 10)];
Hier analysieren wir die droppableId (die wir auf den Index der Objekte setzen) und verwenden sie, um das Objekt aus dem meinObjekt Array.
Sobald wir das richtige Objekt haben, können wir eine Darstellung des gezogenen Elements erhalten, indem wir es in der Liste finden von alleArtikel.
const destObjItem = destObj.allItems.items!.find(item => { return item.id === result.draggableId; });
Beachten Sie, dass wir für jedes Objekt alle Elemente angeben müssen, da Aktionen nur für Elemente aufgerufen werden können, die zum selben Objekt gehören.
Wir können dann eine Aktion auf dem Objekt ausführen:
const actionOnObj = destObj.action!.get(destObjItem!); if (actionOnObj.canExecute) { actionOnObj.execute(); }
Wir führen einige Prüfungen durch, um sicherzustellen, dass das Element in eine Liste gezogen wurde. Unsere endgültige Funktion sollte dann folgendermaßen aussehen:
Funktion onDragEnd(Ergebnis: DropResult): void { const { Quelle, Ziel } = Ergebnis; wenn (!Ziel) { return; } const destObj = meinObjekt[parseInt(Ziel.droppableId, 10)]; const destObjItem = destObj.allItems.items!.find(item => { return item.id === result.draggableId; }); const actionOnObj = destObj.action!.get(destObjItem!); wenn (actionOnObj.canExecute) { actionOnObj.execute(); } }
Damit haben wir nun ein funktionierendes Drag-and-Drop-Kanban-Board. Wir müssen nur noch ein paar Dinge in Studio Pro erledigen. Fügen wir zunächst ein Statusattribut mit unseren verschiedenen Status hinzu.

Fügen wir dann für jeden Status, den wir festlegen möchten, einen Nanoflow hinzu.

Konfigurieren wir nun jeden unserer Abschnitte, indem wir die Elemente so festlegen, dass es sich um alle Elemente mit dem relevanten Status und der richtigen On-Drop-Aktion handelt, und indem wir für „AllItems“ einen Link zu allen Elementen in der Datenbank hinzufügen.

Und wir haben Drag & Drop!

Sortierung
Wir sind fast am Ziel, wir müssen nur noch eine Sache tun, nämlich die Sortierung einführen. Dazu verwenden wir ein nützliches Muster zum Abrufen von Daten aus Pluggable Widgets.
Idealerweise würden wir dies vollständig mithilfe von Werten im „Element“ verwalten, aber derzeit gibt es eine Einschränkung bei der Pluggable Widget API, bei der Attribute für Objekte, die Teil von Listen sind, nicht direkt geändert werden können. Diese Einschränkung soll auf der Roadmap entfernt werden. In der Zwischenzeit können wir den folgenden Ansatz wählen:
Beginnen wir mit der Speicherung der Sortierreihenfolge im Mendix Entität: Artikel.

Während wir dabei sind, werde ich meine ASU aktualisieren, um die Datenbank jedes Mal mit Elementen mit einem Sortierwert neu zu füllen:

Zurück zu unserem Widget: Aktualisieren wir Kanban.xml, um eine neue Eigenschaftengruppe „Sortierung“ zu erhalten, die wir verwenden können, um den vorherigen und aktualisierten Sortierindex zu speichern, wenn wir ein Element ziehen.
Neue Sortierung Wertspeicher sortieren Vorheriges Sortieren Wertspeicher sortieren
Wir können dann unsere onDrag-Funktion aktualisieren, um diese Eigenschaften an unsere Mendix Modell:
Funktion onDragEnd(Ergebnis: DropResult): void { const { Quelle, Ziel } = Ergebnis; wenn (!Ziel) { return; } const destObj = meinObjekt[parseInt(Ziel.droppableId, 10)]; const destObjItem = destObj.allItems.items!.find(Element => { return item.id === Ergebnis.draggableId; });
prevSort.setValue(neu Big (Quelle.index)) newSort.setValue(neu Big (Ziel.index))
const actionOnObj = destObj.action!.get(destObjItem!); if (actionOnObj.canExecute) { actionOnObj.execute(); } }
Kehren wir zurück zu unserem Mendix Modell können wir diese Werte verwenden, um unsere Sortierlogik aufzubauen.
Lassen Sie uns eine Entität einrichten, die unsere vorherigen und neuen Sortierwerte speichert.

Dann verpacken wir unser Widget in eine Datenansicht, die diese Entität bereitstellt:

Wir können dann unser Widget so konfigurieren, dass die Sortierwerte an die Entität übergeben werden.

Nachdem wir unser Widget in die Datenansicht eingebettet haben, können wir nun unseren KanbanHelper als Parameter zu unseren Aktionen hinzufügen, die beim Ziehen ausgeführt werden.

In jedem unserer onDrag-Nanoflows machen wir drei Hauptdinge:
- Shop die Ausgangszustand des Artikels (damit wird für Sortierberechnungen bestimmt, ob sich die Spalte „Artikel geändert hat“)
- Aktualisieren des Elements zum neuen Status und der neueSortierung Wert
- Implementierung Sortierlogik zu den restlichen Artikeln
Für die Sortierlogik können wir sie in einem Sub-Nanoflow erstellen:

Ich werde hier nicht näher darauf eingehen, wie das Ganze zusammengestellt wurde, aber Der Nanoflow ist im Ressourcenbereich der Github Repo.
Dann führen wir unsere Anwendung aus und testen sie ein letztes Mal …

Zusammenfassung
Jetzt haben wir ein Kanban-Board mit allen möglichen Funktionen! Wir haben die Bibliothek react-beautiful-dnd verwendet, die zusammensetzbare Natur von Mendix Widgets und ein paar einfache (oder ziemlich) Nanoflows zum Erstellen eines voll funktionsfähigen, benutzerdefinierbaren Kanban-Boards.
Den Endzustand dieses Widgets finden Sie hier: GitHub – joe-robertson-mx/kanban-widget
Wenn Sie dieses Kanban-Board implementieren konnten und zeigen möchten, was Sie erstellt haben, teilen Sie es in den Kommentaren unten.
Als nächstes Wir werden uns mit der Umsetzung befassen Karten in unseren Pluggable Widgets und erkunden Sie dabei, wie Sie komplexe externe Bibliotheken verwenden und benutzerdefinierte Dateien in unseren Build einbinden.