Créer des widgets dans Mendix avec React Partie 3 - Kanban
Mendix est la plate-forme low code numéro un, qui permet aux créateurs de développer des applications considérablement plus rapidement que le codage traditionnel. Mendix le frontend est construit à l'aide de React et les développeurs peuvent étendre leurs applications à l'aide de widgets enfichables.
Dans ce blog, nous allons découvrir comment apporter des modifications permanentes aux données de votre application à partir de votre widget, en combinant des widgets pour créer des expériences utilisateur fluides, des hooks personnalisés et bien plus encore…
Il s'agit du troisième blog d'une série en plusieurs parties, les blogs 1 et 2 peuvent être trouvés ici : Créez des widgets dans Mendix avec React — Partie 1 — Compteur de couleurs et Créer des widgets dans Mendix avec React Partie 2 — Minuterie.
Avant de commencer, un petit avertissement : pour cette version, nous profitons de certaines fonctionnalités de VS Code, les instructions peuvent varier si vous préférez un autre IDE.
Ce que nous construisons
Mendix a récemment publié Épopées, un nouvel outil de gestion d'histoire complet !
Dans ce cadre, il existe un composant vraiment sympa qui vous permet de glisser-déposer vos histoires. J'ai pensé que ce serait sympa d'essayer de construire quelque chose de similaire.

Il y a quelques choses que chaque tableau Kanban devrait être capable de faire :
- Autoriser les utilisateurs à définir les étapes Kanban
- Pouvoir changer l'étape d'un élément en le faisant glisser
- Pouvoir modifier l'ordre de tri des éléments d'une section en les faisant glisser
Démarrer
Pour commencer, nous créons l'échafaudage du widget en exécutant le générateur de widget pour créer rapidement un échafaudage de widget. Exécutez la commande :
yo @mendix/widget kanban
Lors de la configuration des propriétés du widget, sélectionnez les options par défaut, tout en veillant à utiliser Typescript.
Ensuite, nous allons mettre à jour l’interface entre notre widget et le Mendix en mettant à jour Kanban.xml. Pour ce faire, nous ajoutons une liste d'éléments de source de données pour permettre à l'utilisateur du widget de spécifier ce Mendix entités qu’ils souhaitent utiliser.
Nous avons deux options pour restituer réellement les éléments dans le widget, nous pouvons utiliser le JSX traditionnel, ou nous pouvons permettre à l'utilisateur de créer la conception qu'il souhaite dans MendixLa deuxième option présente trois avantages principaux :
- Empêche de prescrire la structure dans laquelle les données enfants doivent être affichées
- Ne cache pas le code dans les fichiers de widgets compilés plus difficiles à modifier
- Permet aux utilisateurs de profiter de l'éditeur WYSIWG dans Mendix
Rendre la liste
L'option deux l'emporte ! Pour mettre cela en œuvre, nous pouvons utiliser le 'widgets' tag de la API de widgets enfichablesLe fichier Kanban.xml mis à jour contient désormais :
Articles Liste des articles Contenu Widgets utilisant la source de données
Ensuite, nous pouvons mettre à jour notre fichier Kanban.TSX pour qu'il contienne :
fonction d'exportation Kanban({ widgetList, éléments }: KanbanContainerProps): ReactElement { return ( {éléments.éléments && éléments.éléments.map(élément => { return widgetList.get(élément);})} ); }
Il se passe plusieurs choses dans le code ci-dessus, je vais donc les décomposer :
- « Liste de widgets »est notre interface de widget dans le Mendix modèle, si nous faisons un clic droit sur « widgetList » et cliquons sur « Aller à la définition de type », nous pouvons voir notre documentation pour ce type.
exporter déclarer l'interface ListWidgetValue {
* Renvoie les widgets configurés par l'utilisateur sous la forme d'un nœud de réaction rendu en fonction de l'élément d'objet fourni.*
* @param item Instance de {@link ObjectItem} de la source de données liée.*/
obtenir : (élément : ObjectItem) => ReactNode ;
}
Cela nous indique essentiellement qu'il existe une fonction get pour notre widget, qui attend quelque chose appelé ObjectItem afin de restituer un ReactNode
- « Éléments » est notre source de données à laquelle nous pouvons accéder en tant que ListValue (un Mendix-type défini), si nous faisons un clic droit sur « éléments » et cliquons sur « Aller à la définition de type », nous pouvons voir notre documentation pour ce type. À partir de cette documentation, je peux voir que je peux accéder à un tableau d'ObjectItem, en utilisant le .articles propriété
Nous pouvons ensuite utiliser la fonction JS map intégrée pour parcourir chaque élément de la liste des éléments et utiliser la fonction « get » de l'élément pour renvoyer le résultat.
- Nous avons ensuite envelopper notre fonction in accolades pour montrer qu'il s'agit de JavaScript et l'envelopper dans un div parent afin de rendre chaque élément
- Enfin, nous ajoutons un expression ternaire pour éviter d'appeler la fonction dans le cas où il n'y a aucun élément dans la liste.
Tests
Pour tester notre implémentation, nous avons configuré un projet de test dans le dossier « tests » de notre widget, comme nous l'avons fait dans 1 Blog.
Nous ajoutons une entité appelée Produit avec l'attribut string Contenu

Ensuite, nous ajoutons quelques microflux à créer des $Items dans le microflow après démarrage.

Nous pouvons ensuite construire notre widget à partir du terminal avec npm run build .
Quand nous revenons à notre Mendix modèle et appuyez sur F4, notre widget est disponible pour être placé sur notre page d'accueil.

Ici, j'ai ajouté un conteneur simple avec la classe 'carte" et une chaîne affichant le contenu. Et voilà ! Il rend.

Glisser-déposer
Avoir une liste statique est un bon début, mais nous devons pouvoir déplacer ces éléments !
Choisissons notre bibliothèque de glisser-déposer React. Nous pourrions la construire nous-mêmes (et certains intelligents Mendix les développeurs l'ont déjà fait !) mais il y a un très large sélection de bibliothèques bien prises en charge et très utilisées.
Je suis allé avec réagir-belle-dnd car il a un facile à utiliser API et est très riche en fonctionnalités.
Nous importons ensuite notre bibliothèque glisser-déposer en naviguant vers notre répertoire de widgets (./kanban) et en exécutant la commande
npm i react-beautiful-dnd .
D'après la lecture de la documentation, nous avons besoin de trois zones principales pour notre widget :

<DragDropContext />- Encapsule la partie de votre application pour laquelle vous souhaitez activer le glisser-déposer<Droppable />- Une zone dans laquelle on peut déposer quelque chose.<Draggable />- Ce qui peut être traîné
Étape 1 — Glissable
Commençons donc au niveau le plus bas et convertissons nos éléments pour qu'ils soient déplaçables.
Tout d’abord, décomposons notre ItemList en une fonction distincte, mais gardons-la dans notre composant pour faciliter le développement pour l’instant (nous pouvons la déplacer dans un composant distinct plus tard).
fonction d'exportation Kanban({widgetList, éléments}: KanbanContainerProps): ReactElement {
const ItemList = (): ReactElement => {
retour ( {éléments.éléments && éléments.éléments.map(élément => { return widgetList.get(élément);})} );
}
retour ( ) }
Afin de rendre chaque élément de la liste déplaçable, nous devons envelopper le widget dans un composant Draggable, qui attend un identifiant unique pour l'élément déplaçable — un index pour ordonner les éléments, et une fonction enfant pour restituer le composant.
En suivant la documentation de la bibliothèque react-beautiful-dnd pour le reste de l'implémentation, nous nous retrouvons avec le code suivant.
const ItemList = (): ReactElement => { return ( {éléments.éléments &&éléments.éléments.map((élément, i) => { retour ( {fourni => ( {widgetList.get(élément)} )} ); })} ); };
Ce que nous avons fait ici est d'envelopper notre composant widget dans un wrapper « déplaçable » qui nous permet de transmettre une référence et des accessoires.
Les références sont un concept que nous n'avons pas encore abordé dans ces blogs et constituent un élément clé de React. Elles constituent essentiellement un moyen d'accéder aux éléments de votre page et, dans ce cas, sont destinées à garder une trace de ce qui est déplacé : Les refs et le DOM – React.
Étape 2 — Déposable
Maintenant que nous avons nos composants déplaçables, nous devons créer le canevas glisser-déposer.
Pour ce faire, nous devons envelopper notre ItemList de composants déplaçables dans un conteneur déposable en utilisant un modèle très similaire à celui décrit ci-dessus :
{fourni => ( )}
Étape 3 – Contexte
Enfin, nous devons fournir le contexte de glisser-déposer dans lequel la fonctionnalité existe :
{fourni => ( )}
Le contexte s'attend à ce qu'une fonction soit appelée une fois qu'un élément a été déplacé. Pour l'instant, enregistrons simplement que la fonction a été appelée.
Notre code devrait actuellement ressembler à ceci :
fonction d'exportation Kanban({ widgetList, éléments }: KanbanContainerProps): ReactElement { const ItemList = (): ReactElement => { return ( {éléments.éléments && éléments.éléments.map((élément, i) => { retour ( {fourni => ( {widgetList.get(élément)} )} ); })} ); };
fonction onDragEnd(résultat : DropResult) : void { console.log("Traîné"); }
retour ( {fourni => ( )} ); }
Si nous courons npm run build et relancer notre Mendix app, on peut maintenant faire glisser nos cartes ! En quelque sorte…

Nous reviendrons sur ce problème plus tard… la partie la plus importante est que nous pouvons faire glisser nos éléments.
Plusieurs colonnes
Nous pouvons donc faire glisser des éléments vers le haut et vers le bas dans une liste, mais ce que nous voulons vraiment faire, c'est pouvoir faire glisser des éléments entre les colonnes pour déterminer le statut.
Nous voulons que nos utilisateurs du widget puissent spécifier le nombre de colonnes qu'ils souhaitent.
Pour ce faire, nous pouvons utiliser « Objets » dans notre Kanban.xml. Il nous suffit d'envelopper nos propriétés existantes dans une liste d'objets comme ceci :
Sections Nom Nom Éléments de section Liste des articles Contenu Widgets utilisant la source de données
Les plus attentifs d’entre vous remarqueront également que j’ai ajouté un nom pour nous permettre de garder une trace de nos chroniques.
Si vous ouvrez Mendix nous pouvons découvrir notre belle nouvelle interface :

Cool, mais maintenant nous devons rendre nos colonnes, revenons à notre widget…
C'est en fait assez simple.
Tout d’abord, mettons à jour notre JSX pour afficher une colonne pour chaque objet :
{monObjet.map((obj, i) => { return ( {fourni => ( )} ); })}
Nous devrions également faire le ID de dépôt unique et transmettez quelques accessoires à notre ItemList.
{monObjet.map((obj, i) => { return ( {fourni => ( )} ); })}
Cela signifie que nous devons également mettre à jour notre liste d'éléments pour accepter les nouveaux accessoires, et pendant que nous y sommes, nous pourrions aussi bien la refactoriser en quelque chose d'un peu plus facile à lire - je me retrouve avec deux nouveaux fichiers dans mon dossier de composants :
Glissable.TSX
importer { ReactElement, createElement } depuis "react"; importer { Draggable } depuis "react-beautiful-dnd"; importer { ObjectItem, ListWidgetValue } depuis "mendix";
interface d'exportation DraggableProps { élément : ObjectItem ; i : nombre ; widgetList : ListWidgetValue ; }
fonction d'exportation DraggableItem({ élément, i, widgetList }: DraggableProps): ReactElement { retour ( {fourni => ( {widgetList.get(élément)} )} ); }
Et ItemList.TSX
importer { ReactElement, createElement } depuis "react"; importer { ListValue, ListWidgetValue } depuis "mendix"; importer { DraggableItem } depuis "./Draggable";
interface d'exportation ItemListProps { éléments : ListValue ; widgetList : ListWidgetValue ; }
export const ItemList = ({ éléments, widgetList }: ItemListProps): ReactElement => { retour ( éléments && ( {éléments.éléments && éléments.éléments.map((élément, i) => { retour ; })} ) ); };
Stylisme
Nous devons ajouter un peu de style à notre tableau Kanban, pour le transformer d’un vilain petit canard en un beau cygne.
Nous le ferons de deux manières principales :
- Utilisation des classes CSS et la propriété 'className' chaque élément DOM a
- Exploiter les styles en ligne pour utiliser les propriétés calculées dans notre code (c'est-à-dire la largeur de la colonne)
Tout d’abord, mettons à jour notre fichier Kanban.css pour
.kanban-col { marge : 15 px ; remplissage : 10 px ; arrière-plan : #9bedff ; rayon de bordure : 15 px ; }
.kanban-col h6 { alignement du texte : centre ; }
Nous pouvons ensuite utiliser quelques éléments DOM supplémentaires, nos classes et nos styles et nous retrouver avec :
importer { ItemList } depuis "./components/ItemList"; importer { KanbanContainerProps } depuis "../typings/KanbanProps";
importer "./ui/Kanban.css";
fonction d'exportation Kanban({ monObjet }: KanbanContainerProps): ReactElement {
fonction onDragEnd(résultat : DropResult) : void { console.log("traîné"); }
retour ( {monObjet.map((obj, i) => { return ( {fourni => ( {obj.sectionName} {fourni.placeholder} )} ); })} ); }
Attends... qu'est-ce que c'est ? ${100/myObject.length}%
Il s'agit d'un modèle littéral qui vous permet de créer rapidement des chaînes en JS, en écrivant des chaînes dans « et en enveloppant des expressions dans ${}.
Mais revenons au widget… nous avons maintenant des colonnes définies par l’utilisateur, avec des éléments.

Action glisser-déposer
Maintenant, nous voulons que notre glisser-déposer fasse réellement quelque chose…

Pour ce faire, nous allons utiliser l’API d’action Pluggable Widget en combinaison avec une source de données.
Alors, tout d'abord, ajoutons deux nouvelles propriétés à notre objet dans notre Kanban.xml
Éléments de section Liste des articles Tous les articles Liste des articles
En liant notre action à une source de données, nous pouvons exécuter l'action avec un paramètre de MendixCela nous permettra de modifier les attributs de nos articles.
Nous pouvons maintenant mettre à jour notre «surDragEnd ”fonction pour exécuter l'action.
En lisant la documentation de react-beautiful-dnd, nous pouvons voir que onDragEnd reçoit un objet de résultat qui nous permet d'accéder aux draggableId et droppableID. Nous pouvons les utiliser pour déterminer quel élément est en train d'être déplacé et où il est déplacé (et donc quelle action nous devons appeler).
La première chose à faire est de déstructurer notre résultat et de trouver le bon objet :
const { source, destination } = résultat; const destObj = monObjet[parseInt(destination.droppableId, 10)];
Ici, nous analysons le droppableId (que nous définissons sur l'index des objets) et l'utilisons pour récupérer l'objet à partir du monObjet Tableau.
Une fois que nous avons l'objet correct, nous pouvons obtenir une représentation de l'élément déplacé, en le trouvant dans la liste de tous les articles.
const destObjItem = destObj.allItems.items!.find(item => { return item.id === result.draggableId; });
Notez que nous devons spécifier allItems pour chaque objet car les actions ne peuvent être appelées que sur les éléments appartenant au même objet.
Nous pouvons alors exécuter une action sur l'objet :
const actionOnObj = destObj.action!.get(destObjItem!); if (actionOnObj.canExecute) { actionOnObj.execute(); }
Nous effectuons quelques vérifications pour nous assurer que l'élément a été déplacé vers une liste, et notre fonction finale devrait ressembler à ceci :
fonction onDragEnd(résultat: DropResult): void { const { source, destination } = résultat; si (!destination) { retour; } const destObj = monObjet[parseInt(destination.droppableId, 10)]; const destObjItem = destObj.allItems.items!.find(élément => { retour élément.id === résultat.draggableId; }); const actionOnObj = destObj.action!.get(destObjItem!); si (actionOnObj.canExecute) { actionOnObj.execute(); } }
Cela nous a permis de disposer d'un tableau Kanban fonctionnel par glisser-déposer. Il nous reste juste quelques opérations à effectuer dans Studio Pro. Tout d'abord, ajoutons un attribut Statut avec nos différents statuts.

Ajoutons ensuite un nanoflow pour chaque statut que nous souhaitons définir.

Configurons maintenant chacune de nos sections en définissant les éléments comme étant tous les éléments avec le statut approprié et l'action de dépôt correcte, et ajoutons un lien vers tous les éléments de la base de données pour AllItems.

Et nous avons le glisser-déposer !

tri
Nous y sommes presque, il ne nous reste plus qu'une chose à faire : introduire le tri. Pour cela, nous allons utiliser un modèle utile pour extraire des données des widgets enfichables.
Idéalement, nous gérerions cela entièrement à l'aide de valeurs sur l'élément, mais il existe actuellement une limitation avec l'API Pluggable Widget, où les attributs des objets qui font partie de listes ne peuvent pas être directement modifiés. Cette limitation est sur le point d'être supprimée. En attendant, nous pouvons adopter l'approche suivante :
Commençons par stocker l'ordre de tri dans le Mendix entité : Article.

Pendant que nous y sommes, je vais mettre à jour mon ASU pour repeupler la base de données à chaque fois avec des éléments avec une valeur de tri :

Revenons donc à notre widget, mettons à jour le Kanban.xml pour avoir un nouveau groupe de propriétés « Tri », que nous pouvons utiliser pour stocker l'index de tri précédent et mis à jour lorsque nous faisons glisser un élément.
Nouveau tri Trier le stockage des valeurs Tri précédent Trier le stockage des valeurs
Nous pouvons ensuite mettre à jour notre fonction onDrag pour transmettre ces propriétés à notre Mendix modèle:
fonction onDragEnd(résultat : DropResult) : void { const { source, destination } = résultat ; si (!destination) { retour ; } const destObj = monObjet[parseInt(destination.droppableId, 10)]; const destObjItem = destObj.allItems.items!.find(item => { retour item.id === résultat.draggableId ; });
prevSort.setValue(nouveau Big (source.index)) newSort.setValue(nouveau Big (destination.index))
const actionOnObj = destObj.action!.get(destObjItem!); if (actionOnObj.canExecute) { actionOnObj.execute(); } }
Si nous revenons à notre Mendix modèle, nous pouvons utiliser ces valeurs pour construire notre logique de tri.
Configurons une entité pour contenir nos valeurs de tri précédentes et nouvelles.

Enveloppons ensuite notre widget dans une vue de données qui fournit cette entité :

Nous pouvons ensuite configurer notre widget pour transmettre les valeurs de tri à l’entité.

Étant donné que nous avons enveloppé notre widget dans la vue de données, nous pouvons maintenant ajouter notre KanbanHelper comme paramètre à nos actions qui sont exécutées onDrag.

Nous faisons 3 choses principales dans chacun de nos nanoflows onDrag :
- Boutique le statut de départ de l'élément (cela détermine si l'élément a changé de colonne, pour les calculs de tri)
- Mettre à jour l'article au nouveau statut et à la nouveauTrier La valeur
- Mettre en œuvre le logique de tri au reste des articles
Pour la logique de tri, nous pouvons la construire dans un sous-nanoflow :

Je n'entrerai pas dans les détails de la façon dont cela a été mis en place ici, mais le nanoflow peut être trouvé dans la section ressources du Github repo.
Ensuite, lorsque nous exécutons notre application et la testons une dernière fois…

Résumé
Nous avons maintenant un tableau Kanban tout chantant et tout dansant ! Nous avons utilisé la bibliothèque react-beautiful-dnd, la nature composable de Mendix widgets et quelques nanoflows simples (ish) pour créer un tableau Kanban entièrement fonctionnel et définissable par l'utilisateur.
L'état final de ce widget peut être trouvé ici : GitHub – joe-robertson-mx/widget-kanban
Si vous avez réussi à mettre en œuvre ce tableau Kanban et que vous souhaitez montrer ce que vous avez construit, partagez-le dans les commentaires ci-dessous.
Suivant nous allons examiner la mise en œuvre cartes dans nos widgets enfichables, et dans le processus, explorez comment utiliser des bibliothèques externes complexes et inclure des fichiers personnalisés dans notre build.