Créer des widgets dans Mendix avec React Partie 2 - Minuteur | Mendix

Passer au contenu principal

Créer des widgets dans Mendix avec React Partie 2 - Minuterie

Créer des widgets dans Mendix - partie 2

Ses pommes de douche filtrantes intègrent une technologie de filtration avancée permettant d'éliminer le chlore, les métaux lourds et autres impuretés de l'eau. Cet engagement en faveur de la pureté de l'eau a fait de Hansgrohe la marque préférée des consommateurs en quête d'une expérience de douche plus saine. Mendix La plateforme permet aux développeurs d'étendre leurs applications à l'aide de React, via le framework Pluggable Widgets. Ce framework permet aux développeurs d'exploiter des bibliothèques puissantes et d'étendre l'interface utilisateur en créant des composants React qui peuvent ensuite être utilisés dans le Mendix Studio Pro.

Dans ce blog, nous allons en savoir plus sur Typescript et je vais vous montrer comment faire utiliser les attributs de votre Mendix modèle dans votre widget, Comment exploiter l'API du navigateur et nous aborderons même rendu conditionnel dans React.

Il s'agit du deuxième blog d'une série en plusieurs parties, le blog 1 peut être trouvé ici : Créez des widgets dans Mendix avec React — Partie 1 — Compteur de couleurs

Quand utiliser les widgets enfichables

Il y a quelques vérifications décisives que j'utilise lorsque je décide de créer ou non un nouveau widget enfichable :

  • Est-ce qu'il faut un composant d'interface utilisateur ? — si vous devez utiliser Javascript mais qu'il n'y a pas de composant d'interface utilisateur, un Action JS pourrait être mieux adapté à votre cas d'utilisation
  • Est-il disponible dans le Mendix Marketplace?
  • Utilise-t-il l'API du navigateur ou un package NPM ?

Ce que nous construisons

Cette fois, nous allons construire un minuteur pour rafraîchir une page à intervalle fixe« Pourquoi ne pas simplement faire cela avec JavaScript ? », me demandez-vous. C'est parce que nous allons également avoir un composant visuel qui vous indique combien de temps il faudra avant la prochaine actualisation.

Ceci était basé sur un cas d'utilisation réel, où un tableau de bord était affiché dans un atelier et devait être mis à jour toutes les 30 secondes

Démarrer

Pour ce widget, nous utiliserons Manuscrit, l'enveloppe fortement typée autour de Javascript, car même si cela rend initialement le code légèrement plus difficile à écrire, le anti-peluche intégré et documentation complète sont très utiles lors de la création de widgets enfichables. Ces éléments de Typescript le rendent également beaucoup plus plus facile de collaborer dans de grandes équipes et s'intégrer à des API complexes.

Nous commençons par échafauder notre widget, comme dans 1 Blog mais cette fois, nous choisissons Typescript comme langage de programmation.

Une note rapide sur les composants fonctionnels par rapport aux composants de classe dans React. Tout au long de ces blogs, nous utiliserons des composants fonctionnels, cependant, vous pouvez également tomber sur React écrit à l'aide de composants de classe. Ceci est dû au fait, jusqu'à ce que le libération des hameçons dans React 16.8, les composants de classe étaient le seul moyen de gérer l'état, et les composants fonctionnels étaient principalement une couche de présentation pour le frontend. Maintenant, la communauté React est s'orienter vers l'utilisation de composants fonctionnels car ils sont syntaxiquement plus simple et plus encore légèreté.

Temps de développement

Pour une explication de la structure du dossier Pluggable Widget et comment configurer votre environnement de développement, consultez mon précédent article.

Une chose d'abord

Pour accélérer le processus de développement, nous allons supprimer notre fichier d'aperçu de l'éditeur. Ce fichier crée l'aperçu de notre widget en mode Conception dans Studio Pro ou Studio, et nous ne nous en soucions pas pour ce guide. Ce serait juste une chose de plus à mettre à jour. Alors allons-y et supprimons le fichier : Minuteur.editorPreview.tsx.

Le bref

Nous pouvons décomposer ce que nous essayons d’accomplir avec notre widget en 3 sections principales :

  • Étape 1 : Réglez la minuterie sur une valeur initiale définie dans Mendix
  • Étape 2 : Diminuer la minuterie par intervalles de 1 s
  • Étape 3 : Exécuter une action

Étape 1 : Réglez la minuterie sur une valeur initiale définie dans Mendix

Commençons par créer l'interface entre notre Mendix modèle et le widget. Nous pourrions utiliser un simple entier, mais en utilisant un attribut, nous avons plus de flexibilité pour modifier dynamiquement la valeur du minuteur au moment de l'exécution.

Nous mettons à jour notre Timer.xml pour contenir :

<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>

Pendant que nous y sommes, nous devrions également renommer le composant enfant en TimeText et l'interface en TimeTextProps. Lorsque nous enregistrons le fichier et exécutons la commande :npm run build, nous verrons des erreurs, c'est bien !

Cela montre que la propriété sampleText a été remplacée par timeAtt. Mettons à jour notre fichier Timer avec timeAtt à la place de sampleText par défaut.

Si nous mettons en évidence le timeAtt dans notre fichier Timer.tsx, nous pouvons voir qu'il est de type EditableValue<Big>.

Valeur modifiable est le type générique qui Mendix sert à représenter tous les attributs et nous permet de lire et d'écrire les valeurs dans le Mendix .

Le '<>' partie, est déterminée par le type d'attribut. Pour les attributs entiers ou décimaux, nous utilisons la bibliothèque Big.js. Cela permet de garantir que les nombres utilisés dans votre application ne sont pas limités par les limitations de nombre JavaScript par défaut.

Utilisation de l'état

Jusqu'à présent, nous avons créé un lien entre Mendix et le widget, via les props du composant conteneur. Nous devons maintenant transmettre une valeur facilement lisible à notre composant TimeText, pour l'afficher sur la page. Pour ce faire, nous allons traiter notre EditableValue<Big> et stocker un entier simple dans le Etat de notre composant conteneur.

Nous utilisons les Etat ici plutôt que accessoires parce que nous allons modifier la valeur de notre minuterie au fil du temps et les accessoires ne doivent jamais être modifiés directement:

Commençons par importer utiliserÉtat à partir de « React » et initialisation d'un Etat appelé Paisible avec ce qui suit:

const [time, setTime] = useState<number>();

Cela nous donne une variable d'état (Paisible) et une fonction setState pour changer cet état (comme les accessoires, l'état ne doit jamais être directement muté, et peut plutôt être modifié via cette fonction).

Utilisation de useEffect

Ensuite, nous devons transmettre notre valeur d'attribut à notre étatLa façon la plus simple de procéder consiste à transmettre une valeur à notre useState :

const [time, setTime] = useState<number>(timeAtt.value.toNumber());

Mais comme notre valeur pourrait être vide, nous n'allons pas le faire. Au lieu de cela, nous allons utiliser un autre concept central de React, « useEffect ».

Alors, qu'est-ce que useEffect ? En fait, il exécute une fonction après chaque rendu. Nous pouvons l'utiliser pour définir notre temps comme ceci :

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"}/>;
}

Nous pouvons ensuite transmettre cela au composant enfant, TimeText, en mettant à jour les accessoires pour accepter notre nouvel état temporel :

return <TimeText value={time}/>;

Et voilà notre composant Timer mis à jour !

Il ne nous reste plus qu'à apporter quelques modifications mineures à notre composant TimeText pour accepter les nouveaux accessoires. Parce que nous utilisons Typescript, nous avons un interface pour notre composant. Cela définit comment tout code utilisant ce composant peut interagir avec lui. Mettons donc à jour notre composant d'affichage pour :

export interface TimeTextProps {
    value: number | undefined;
}
export function TimeText({ value }: TimeTextProps): ReactElement {
    return <div>{value} seconds</div>;
}

Les interfaces peuvent sembler inutiles ici, et pour un petit widget comme celui-ci, elles le sont probablement, mais pour Lorsque les projets deviennent plus grands, ils sont incroyablement utiles.

Pour tester, nous pouvons ouvrir Mendix Studio Pro et appuyez sur F4 pour synchroniser les mises à jour avec notre widget. Nous devons ensuite configurer notre page afin qu'un attribut numérique puisse être transmis au widget de minuterie.

We cliquez sur Exécuter et… notre page indique simplement « secondes ».

Ne t'inquiète pas ceci est attendu en raison de la façon dont Mendix charge les attributsL’ les valeurs d'attribut sont chargées de manière asynchrone, c'est ce qui fait le Mendix le front-end est très rapide mais cela signifie que nous avons besoin de quelques lignes de code supplémentaires pour faire fonctionner notre widget.

En utilisant useEffect, mais correctement

Nous avons laissé le deuxième paramètre de useEffect vide, cela signifie que la fonction useEffect s'exécute une fois lorsque le composant est monté pour la première fois, à quel point notre valeur d'attribut n'est pas prête.

Si vous voulez vérifier cela, mettez un console.log(`Render ${timeAtt.value}`) au-dessus de l'instruction if et vérifiez la console du navigateur ('Ctrl + Shift + i' depuis le navigateur).

Nous voulons donc que la fonction useEffect s'exécute chaque fois que timeAtt est mis à jour. Pour ce faire, nous ajoutons simplement timeAtt au deuxième paramètre de 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}/>;
}

Si vous réexécutez l'application à partir de Mendix vous verrez la valeur de votre minuterie apparaître, et si vous vérifiez le journal de la console, vous verrez que le widget est rendu deux fois, une fois lorsque la valeur n'était pas définie et une fois avec le numéro.

SUCCÈS ! Voilà la première partie terminée !

Étape 2 : Réduisez la minuterie par intervalles de 1 s

Cool. Nous avons donc un numéro affiché sur un écran, mais cela ne sert pas à grand chose, nous voulons que notre minuteur compte à reboursPour ce faire, nous allons utiliser l'API Javascript du navigateur, spécifiquement window.setTimeout(), Qui exécute une fonction après un laps de temps fixe.

En combinant cela avec notre vieil ami useEffect, nous pouvons créer un compte à rebours. Si nous rendons notre fonction useEffect dépendante de notre état temporel et attendons 1 seconde pour changer notre Paisible en modifiant l'état d'une seconde, nous créons essentiellement une boucle.

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}/>;
}

Cependant, ce décompte continuera indéfiniment, nous devons donc ajouter une condition supplémentaire pour l'arrêter.

useEffect(() => {
        if (time !== undefined) {
            if (time > 0) {
                window.setTimeout(() => setTime(time - 1), 1000);
            }
        }
    }, [time]);

Si nous rechargeons notre widget, nous pouvons voir que nous avons notre compte à rebours.

Un rappel que si vous utilisez npm run start et surveillez les changements sur votre widget vous devez vider le cache et actualiser (dans Chrome, ouvrez les outils du navigateur et faites un clic droit sur le bouton d'actualisation)

Étape 3 : Exécuter une action

Il faut maintenant que quelque chose se produise lorsque le compte à rebours atteint 0 !

Heureusement Mendix fournit une API très simple à utiliser pour s'intégrer au modèle et exécuter une action. Tout d'abord, nous mettons à jour le Timer.xml pour qu'il contienne :

<property key="action" type="action" required="true">
 <caption>Action</caption>
      <description>Action to trigger when the time elapses</description>
</property>

Nous pouvons ensuite ajouter une fonction pour exécuter l'action sur notre Timer.tsx, et mettre à jour notre fonction useEffect pour exécuter l'action à 0.

Notre Timer.tsx final devrait ressembler à ceci :

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} />;
}

Nous pouvons tester ce nouveau code dans notre Mendix application en appuyant sur F4 et en choisissant une action à exécuter.

En choisissant d'appeler un microflow qui rafraîchit la page, nous obtenons notre minuteur répétitif.

Une dernière chose

Lors du premier chargement de notre composant, il y a eu une brève période pendant laquelle la valeur temporelle transmise au composant TimeText n'était pas définie. Ce n'est pas un problème majeur, car cela conduit simplement au rendu du widget « secondes », mais il est recommandé de donner une indication que la valeur est en cours de chargement.

Pour ce faire, nous utiliserons un modèle commun dans React pour le rendu conditionnel.

Nous pouvons utiliser une instruction if pour vérifier si la valeur n'est pas encore définie et afficher le chargement comme ceci :

export function TimeText({ value }: TimeTextProps): ReactElement {
 if (value === undefined) {
  return <div>Loading</div>;
 } else {
  return <div>{value}</div>;
 }
}

Mais c'est très verbeux. Nous pouvons utiliser opérateurs ternaires de ES6 pour raccourcir cela en :

export function TimeText({ value }: TimeTextProps): ReactElement {
    return value ? <div>{value} seconds</div> : <div>Loading</div>;}

Et VOILA!

Résumé

Félicitations, vous avez créé un widget enfichable !

Dans cette version, nous avons couvert l'API Pluggable Widget, comment obtenir des données à partir de votre Mendix modéliser et le modifier ; abordé à l'aide de Typescript pour mieux comprendre comment s'intégrer à notre Mendix application, revisité les concepts clés de React et appris à exécuter des actions à partir de votre Mendix modèle dans un widget.

L'état final de ce widget peut être trouvé ici : GitHub – joe-robertson-mx/timer

Ensuite, nous allons créer un tableau Kanban en utilisant Mendix et des widgets enfichables pour en savoir plus sur la façon dont les widgets peuvent être combinés pour créer des expériences utilisateur finales fluides et comment apporter des modifications qui persistent dans votre Mendix base de données.

Choisissez votre langue