Suivez votre progression
Accueil Trailhead
Accueil Trailhead

Connexion des composants grâce aux événements

Objectifs de formation

Une fois cette unité terminée, vous pourrez :
  • Définir des événements personnalisés pour vos applications.
  • Créer et déclencher des événements depuis un contrôleur de composant.
  • Créer des gestionnaires d’actions pour attraper et traiter des événements envoyés par d’autres composants.
  • Refactoriser un gros composant en composants plus petits.

Connexion des composants grâce aux événements

Dans cette unité, nous aborderons la dernière fonctionnalité qui n’est pas terminée de notre petite application expenses : la case à cocher Remboursé ?. Vous pensez probablement que l’implémentation d’une case à cocher sera rapide. Nous pourrions certainement prendre quelques raccourcis et en faire un sujet très bref.

Mais en plus d’expliquer comment faire fonctionner la case à cocher, cette unité élimine également les raccourcis que nous avons pris pour arriver jusqu’ici. Dans le cadre de cette unité, nous « ferons les choses correctement ». Cela signifie que dans certaines situations, il nous faudra refactoriser ce que nous avons développé précédemment.

Avant de commencer, parlons d’abord des raccourcis que nous avons pris, de la Bonne démarche à suivre et des raisons pour lesquelles cette Bonne démarche est (un peu) plus difficile, mais aussi également meilleure.

Composition et décomposition

Si vous examinez le code source de notre petite application Expenses, et si vous listez les différents éléments du code, vous obtiendrez quelque chose qui ressemble à ce qui suit :

  • Composant expenses
    • expenses.cmp
    • expensesController.js
    • expensesHelper.js
  • Composant expensesList
    • expensesList.cmp
  • Composant expenseItem
    • expenseItem.cmp
  • ExpensesController (côté serveur)
    • ExpensesController.apex

Voici les éléments réunis, avec les événements createExpense et updateExpense que vous allez raccorder plus tard.

L'application Expenses est formée de nombreux petits composants.

Mais si vous examinez l’application à l’écran, que voyez-vous ? Ce que vous devriez voir, et que vous retrouverez partout, c’est que l’application se divise en beaucoup plus de composants. Vous constaterez que vous pouvez décomposer encore davantage notre application, en plus petits éléments, que nous ne l’avons fait jusqu’à présent. Au minimum, espérons que vous remarquerez que le formulaire Add Expense devrait vraiment être dans son propre composant indépendant (c’est la raison pour laquelle il est encadré dans l’interface utilisateur !).

Pourquoi n’en avons-nous pas fait directement un composant distinct ? C’est le plus gros raccourci que nous ayons pris au cours de ce module. En termes de conception logicielle, c’est pire que le bidouillage que nous avions qualifié plus haut de « dégoûtant ». Le bon moyen de développer une application reposant sur des composants Lightning consiste à créer des composants indépendants puis à les réunir pour constituer de nouvelles fonctionnalités à plus haut niveau. Pourquoi n’avons-nous pas adopté cette approche ?

Nous avons pris un raccourci en gardant le formulaire Add Expense dans le code principal du composant expenses, de manière à garder dans le même composant l’attribut de composant Tableau expenses principal et le code de contrôleur qui l’affecte. Nous voulions que la fonction d’assistance createExpense() puisse atteindre directement le tableau expenses. Si nous avions décidé d’inclure le formulaire Add Expense dans un composant distinct, cela n’aurait pas été possible.

En quoi cela aurait-il été un problème ? Nous l’avons déjà brièvement évoqué, mais voudrions vraiment insister dessus maintenant. Les composants Lightning sont censés être autonomes. Ils doivent être des éléments indépendants, qui englobent la totalité de leurs fonctionnalités essentielles. Cependant, un composant ne peut en aucun cas modifier la structure interne d'un autre, même s’il s’agit de l’un de ses composants enfants.

Il y a deux manières principales d’interagir avec un autre composant ou de l’affecter. La première, que nous avons déjà abordée et utilisée à plusieurs reprises : définir des attributs de la balise du composant. Les attributs publics d’un composant constituent une partie intégrante de son API.

Le deuxième moyen d’interagir avec un composant consiste à le faire via des événements. Tout comme les attributs, les composants déclarent les événements qu’ils transmettent et peuvent gérer. Et tout comme pour les attributs, ces événements publics constituent une partie de l’API publique du composant. En réalité, nous avons déjà utilisé et géré des événements, mais ces événements étaient dissimulés derrière certaines fonctionnalités pratiques. Dans cette unité, nous nous intéresserons aux événements et en créerons nous-mêmes quelques-uns.

La métaphore du câblage de circuit, une fois encore

Ces deux mécanismes, Attributs et Événements, sont les « sockets » de l’API, qui vous permettent de relier les composants pour constituer des circuits complets. En coulisses, les événements représentent également les électrons qui parcourent ce circuit. Mais ce n’est que l’une des différences entre événements et attributs.

Lorsque vous définissez l’attribut onclick d’un composant <lightning:button> pour qu’il corresponde au gestionnaire d’actions d’un contrôleur de composant, vous créez une relation directe entre ces deux composants. Ils sont liés, et bien qu'ils utilisent des API publiques pour rester indépendants l’un de l’autre, ils n’en sont pas moins connectés.

Les événements sont différents. Les composants n’envoient pas d’événements à un autre composant. Les événements ne fonctionnent pas ainsi. Les composants émettent des événements d’un type particulier. Si un composant réagit à ce type d’événement, et si ce composant « entend » votre événement, il y réagira.

Vous pouvez vous représenter la différence entre les attributs et les événements comme la différence entre des circuits filaires et des circuits sans fil. Et il n’est pas question de téléphones sans fil. Un composant n’obtient pas le « numéro » d’un autre composant pour l’appeler. Il s’agirait alors d’un attribut. Non. Les événements seraient plutôt similaires à des émissions sans fil. Votre composant passe à la radio et transmet un message. Mais quelqu’un a-t-il allumé sa radio et l’a-t-il réglée sur la bonne fréquence ? Votre composant n’a aucun moyen de le savoir, vous devez donc développer vos composants de manière à ce que tout se passe bien si personne n’entend les événements qu’ils émettent (certaines choses peuvent ne pas fonctionner, mais rien ne doit planter).

Envoi d’un événement à partir d’un composant

Assez de théorie, passons maintenant à un usage concret de notre application et voyons comment les événements fonctionnent dans le code. Nous commencerons par implémenter la case à cocher « Remboursé ? ». Ensuite, nous utiliserons ce que nous avons appris pour refactoriser le formulaire Add Expense dans son propre composant, comme l’exigent les meilleures pratiques.

Pour commencer, examinons le gestionnaire de clics du composant <lightning:input> du champ Reimbursed__c.

<lightning:input type="toggle"
            label="Reimbursed?"
            name="reimbursed"
            class="slds-p-around_small"
            checked="{!v.expense.Reimbursed__c}"
            messageToggleActive="Yes"
            messageToggleInactive="No"
            onchange="{!c.clickReimbursed}"/>

Avant de nous intéresser plus en détail au gestionnaire de clics, prenons un peu de recul pour avoir un aperçu de ce que <lightning:input> a à offrir. type="toggle" constitue une case à cocher sous la forme d’un commutateur. class vous permet d’appliquer un style CSS personnalisé ou d’utiliser des utilitaires SLDS. messageToggleActive et messageToggleInactive sont des étiquettes personnalisées correspondant aux positions cochées et non cochées. Ces attributs pratiques ne sont que quelques-uns de ceux dont dispose <lightning:input>. Pour terminer, l’attribut onchange de <lightning:input> permet de raccorder aisément le commutateur à un gestionnaire d’actions. Celui-ci contrôle la mise à jour de l’enregistrement selon que vous déplacez le commutateur vers la droite (activé) ou vers la gauche (désactivé).

Observons maintenant ce qu'il se passe lorsqu'il est activé ou désactivé. D’après le code que nous avons écrit pour créer une nouvelle dépense, la mise à jour d’une dépense devrait ressembler à ce qui suit :

  1. Récupérer l’élément expense qui a été modifié.
  2. Créer une action de serveur pour mettre à jour l'enregistrement de dépense sous-jacent.
  3. Insérer l’élément expense dans l’action.
  4. Configurer un rappel pour gérer la réponse.
  5. Déclencher l’action et envoyer la requête au serveur.
  6. Mettre à jour l’attribut expenses lorsque la réponse arrive et que le rappel s’exécute.

Mais de quel attribut expenses parle-t-on ? Réexaminons le balisage de notre composant. Pas de expenses au pluriel, juste un expense. Ah oui c’est vrai, ce composant n’est donc destiné qu’à un seul élément. Il y a bien un attribut expenses dans le composant expensesList… Mais ce n’est même pas le « véritable » expenses. Le vrai est un attribut de haut niveau du composant expenses. Hum.

Y a-t-il un component.get("v.parent") ? Sinon, faudrait-il que le balisage se présente sous la forme component.get("v.parent").get("v.parent") + quelque chose qui nous permettrait d’obtenir une référence au parent de notre parent, afin que nous puissions paramétrer ici l’attribut expenses ?

Arrêtons-nous. Ici. OK, aucun problème.

Les composants ne peuvent pas entrer en relation avec d’autres composants pour régler leurs valeurs. Il n’y a aucun moyen de dire « Hey, grand-père, je vais mettre à jour expenses ». Les composants se mêlent de leurs propres affaires. Lorsqu’un composant souhaite qu’un composant parent apporte une modification, il le lui demande. Gentiment. En envoyant un événement.

Voici la partie sympa. L’envoi d’un événement est quasi identique à la gestion directe de la mise à jour. Voici le code du gestionnaire clickReimbursed.

({
    clickReimbursed: function(component, event, helper) {
        let expense = component.get("v.expense");
        let updateEvent = component.getEvent("updateExpense");
        updateEvent.setParams({ "expense": expense });
        updateEvent.fire();
    }
})

Impressionnant. C’est très simple ! Et cela ressemble beaucoup à ce que nous avons envisagé ci-dessus. Le code ci-dessus de clickReimbursed fonctionne ainsi :

  1. il obtient expense, qui a été modifié ;
  2. il crée un événement nommé updateExpense ;
  3. il encapsule expense dans l’événement ;
  4. il déclenche l’événement.

La partie rappel est manquante, mais à part cela, vous êtes en terrain connu. Mais qui se charge d’appeler le serveur, de gérer la réponse du serveur et de mettre à jour l’attribut Tableau expenses ? Et d’où vient cet événement updateExpense ?

updateExpense est un événement personnalisé, c’est-à-dire un événement que nous avons créé nous-mêmes. On peut le voir au fait que nous utilisons component.getEvent(), au lieu de component.get() pour obtenir une action du serveur. Par ailleurs, ce que nous obtenons ne provient pas d’un fournisseur de valeurs, il s’agit simplement d’un nom. Nous définirons cet événement dans un instant.

Parlons-en, tout comme nous avons abordé l’élément qui allait gérer l’appel du serveur et la réponse du serveur. Nous pourrions implémenter la requête serveur et gérer la réponse dans le composant expenseItem. Dans ce cas, nous enverrions un événement uniquement pour mettre à jour l’affichage des choses qui dépendent du tableau expenses. Ce choix serait parfaitement valide et préserverait l’autonomie du composant expenseItem, ce qui est une bonne chose.

Cependant, comme nous le verrons, le code de création d’une nouvelle dépense et le code de mise à jour d’une dépense existante sont très similaires, suffisamment pour que nous préférions éviter de dupliquer le code. Le choix de conception que nous avons fait consiste donc à envoyer un événement updateExpense que le composant expenses principal gérera. Plus tard, lorsque nous refactoriserons notre formulaire, nous ferons la même chose pour créer une nouvelle dépense.

Tous les composants enfants délégant la responsabilité de gérer les demandes serveur et l’attribut Tableau expenses, nous subdivisons quelque peu l’encapsulation. Mais si vous considérez ces composants enfants comme étant les détails d’implémentation interne du composant expenses, aucun problème. Le composant expenses principal est autonome.

Vous avez le choix : soit consolider la logique critique, soit encapsuler. Il vous faudra faire des compromis dans les composants Aura, tout comme vous le feriez dans n’importe quelle conception logicielle. Il vous suffit de veiller à bien documenter les détails.

Définition d’un événement

La première chose que nous ferons, c’est définir notre événement personnalisé. Dans la Developer Console, sélectionnez Fichier | Nouveau | Événement Lightning, puis nommez l’événement « expensesItemUpdate ». Remplacez le contenu par défaut par le balisage suivant :

<aura:event type="COMPONENT">
    <aura:attribute name="expense" type="Expense__c"/>
</aura:event>

Il existe deux types d’événement : les événements Composant et les événements Application. Ici, nous utilisons un événement Composant, car nous voulons qu’un composant parent attrape de l’événement pour le gérer. Un composant parent est un composant hiérarchiquement supérieur à cet événement dans la hiérarchie des composants. Si nous souhaitions un événement de type « diffusion générale », pouvant être reçu par tout composant, nous utiliserions plutôt un événement Application.

Nous ne pouvons pas aborder toutes les différences entre les événements Composant et les événements Application et toutes leurs bonnes méthodes d’utilisation dans ce module. C’est un sujet plus avancé, et la complexité des détails pourrait nous distraire de l’objectif de ce module. Lorsque vous serez prêt à en apprendre davantage, les Ressources seront là pour vous aider.

Un autre point remarquable concernant les événements, c’est le point auquel leur définition est compacte. Nous avons nommé l’événement expensesItemUpdate lorsque nous l’avons créé, et son balisage comprend des balises <aura:event> de début et de fin, ainsi qu’une balise <aura:attribute>. Les attributs d’un événement décrivent la charge utile qu’il peut gérer. Dans le gestionnaire d’actions clickReimbursed, nous définissons la charge utile via un appel de setParams(). Ici, dans la définition d’événement, nous pouvons observer la manière dont le paramètre d’événement est spécifié, et qu’il n’y a aucun autre paramètre valide.

Et c’est pratiquement tout ce qu’il y a à faire pour définir des événements. Les événements eux-mêmes n'ont pas besoin de détails d’implémentation ou de comportement. Ce ne sont que des packages. Certains événements n’ont même absolument aucun paramètre. Ce ne sont que des messages. « Cet événement s’est produit ! » Tous les comportements à adopter si l’événement se produit sont définis dans les composants qui envoient et reçoivent l’événement.

Envoi d’un événement

Nous avons déjà vu comment déclencher un événement dans le gestionnaire d’actions clickReimbursed. Mais il reste une dernière chose à faire pour que cela fonctionne : inscrire l’événement. Ajoutez la ligne suivante au composant expenseItem, immédiatement en dessous de ses définitions d’attributs.

    <aura:registerEvent name="updateExpense" type="c:expensesItemUpdate"/>

Ce balisage indique que notre composant déclenche un événement nommé « updateExpense », qui est de type « c:expensesItemUpdate ». Mais le nom de l’événement n’était-il pas « expensesItemUpdate » lorsque nous l’avons défini ? Et que s’est-il passé au niveau des types de composant ou d’événement d’application ?

Vous avez raison de penser que c’est un peu déroutant, c’est un peu comme un tour de passe-passe. Le plus simple est peut-être d’envisager les « applications » et « composants » comme des types d’événements de l’infrastructure de composants Aura, tandis que les types provenant des noms des événements définis sont des types d’événements personnalisés, ou des types de structures d’événements. Cela signifie que lorsque vous définissez un événement, vous définissez un format de package. Lorsque vous procédez à une inscription afin d’envoyer un événement, vous déclarez le format utilisé.

Il est possible que le processus de définition et d’inscription d’un événement vous semble encore un peu bizarre, alors anticipons un peu. Ici, dans expenseItem, nous allons envoyer un événement nommé updateExpense. Ensuite, dans expenseForm, nous ferons la même chose pour un événement nommé createExpense. Ces deux événements doivent inclure une dépense à enregistrer sur le serveur. Ils emploient donc tous deux le type d’événement ou le format de package c:expensesItemUpdate pour envoyer leurs événements.

Côté réception, nous allons procéder à une inscription pour que notre composant expenses principal gère ces deux événements. Bien que l’appel serveur s’avère en définitive identique, les mises à jour intervenant dans l’interface utilisateur sont légèrement différentes. Alors, comment expenses sait-il s’il faut créer ou mettre à jour la dépense dans le package c:expensesItemUpdate ? Grâce au nom de l’événement envoyé.

Tout deviendra plus clair dans votre compréhension des composants Lightning une fois que vous aurez assimilé cette distinction et la manière dont un événement peut être utilisé dans plusieurs objectifs. Si vous n’en êtes pas encore tout à fait à ce stade, vous y parviendrez en examinant le reste du code.

Avant que nous ne passions à la gestion des événements, récapitulons ce qu’il faut pour les envoyer.

  1. Définir un événement personnalisé en créant un événement Lightning, et en lui affectant un nom et des attributs.
  2. Inscrire votre composant pour envoyer ces événements, en choisissant un type d’événement personnalisé et en donnant un nom à cette utilisation précise de ce type.
  3. Déclenchez l’événement dans le code de votre contrôleur (ou de votre assistance) de la manière suivante :
    1. En utilisant component.getEvent() pour créer une instance d’événement spécifique.
    2. En envoyant l’événement avec fire().

Si vous avez implémenté tout le code que nous venons d’étudier, vous pouvez le tester. Rechargez votre application et cochez / décochez une case « Remboursé ? » à plusieurs reprises. Si vous avez manqué une étape, vous obtiendrez une erreur et devrez vérifier votre travail. Si vous avez tout fait correctement, la dépense change de couleur en fonction de son statut « Remboursé ? », exactement comme prévu !

Ce comportement était déjà observable auparavant, même avant que nous ne commencions cette unité. C’est l’effet du composant <lightning:input> lorsque sa valeur est définie ainsi : value="{!v.expense.Reimbursed__c}". Lorsque vous activez ou désactivez la case, la version locale de expense est mise à jour. Mais cette modification n’est pas envoyée jusqu’au serveur. Si vous examinez l’enregistrement expense dans Salesforce, ou si vous rechargez l’application, vous ne verrez pas la modification.

En quoi cela aurait-il été un problème ? Nous n’avons fait que la moitié du travail de création d’un circuit complet pour notre événement. Il nous faut terminer de câbler le circuit en créant le gestionnaire d’événement de l’autre côté. Ce gestionnaire d’événement se chargera de l’envoi de la modification au serveur, rendant ainsi la mise à jour durable.

Gestion d’un événement

Trois étapes sont nécessaires pour permettre au composant expenseItem d’envoyer un événement. De même, trois étapes parallèles sont nécessaires pour permettre au composant expenses de recevoir et de gérer des événements.

  1. Définir un événement personnalisé. Nous l’avons déjà fait, car expenseItem envoie le même événement personnalisé que celui reçu par expenses.
  2. Inscrire le composant pour gérer l’événement. Ainsi, vous associerez l’événement à un gestionnaire d’actions.
  3. La gestion de l'événement proprement dite par un gestionnaire d’actions.

Comme nous avons déjà réalisé l’étape 1, tournons-nous immédiatement vers l’étape 2, et inscrivons expenses de manière à ce qu’il reçoive et gère l’événement updateExpense. Tout comme l’inscription donnant la possibilité d’envoyer un événement, celle permettant d’en gérer un consiste en une seule ligne de balisage que vous devez ajouter au composant expenses immédiatement après le gestionnaire init.

     <aura:handler name="updateExpense" event="c:expensesItemUpdate"
        action="{!c.handleUpdateExpense}"/>

À l’instar du gestionnaire init, cela implique l’utilisation de la balise <aura:handler>, ainsi que d’un attribut action définissant quel est le gestionnaire d’actions de contrôleur pour cet événement. Tout comme vous avez inscrit l’événement dans expenseItem, vous devez définir le nom et le type d’événement. Notez l’utilisation d’un attribut nommé event pour le type, ce qui est nettement plus logique.

En d’autres termes, rien de bien nouveau. Parmi les rares nouveautés spécifiques à la gestion d’événements personnalisés : la manière dont les attributs se combinent, et déterminer comment faire correspondre le « socket » récepteur de expenses et le « socket » expéditeur de expenseItem.

La partie « câblage » de cette tâche est maintenant terminée. Il ne reste plus qu’à développer le gestionnaire d’actions proprement dit !

Nous commencerons par le gestionnaire d’actions handleUpdateExpense. Voici le code, et n’oubliez pas de l’insérer juste en dessous du gestionnaire d’actions clickCreate.

    handleUpdateExpense: function(component, event, helper) {
        let updatedExp = event.getParam("expense");
        helper.updateExpense(component, updatedExp);
    }

Oh. Voilà qui est intéressant. À l’exception de la vérification de validation du formulaire et de la fonction d’assistance spécifique à laquelle nous avons délégué le travail, ce gestionnaire d’actions ressemble beaucoup à handleCreateExpense.

Et maintenant, ajoutons la fonction d’assistance updateExpense. Comme pour le gestionnaire d’actions, veillez à bien insérer ce code immédiatement en dessous de la fonction d’assistance createExpense.

    updateExpense: function(component, expense) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                // do nothing!
            }
        });
        $A.enqueueAction(action);
    },

Il y a deux choses que vous devriez immédiatement remarquer. D’une part, à l’exception des spécificités du rappel, la méthode d’assistance updateExpense est identique à la méthode d’assistance createExpense. Il semblerait que cela soit une bonne opportunité.

D’autre part, concernant les spécifiés de rappel en question : Comment est-ce que cela peut fonctionner ? Comment la bonne chose à faire peut-elle être rien ?

Pensez-y un moment. Précédemment, lorsque nous avons testé l’envoi de l’événement (si ce n’est avant), nous avons vu que la couleur du composant expenseItem changeait en réponse à l’activation / la désactivation de la case à cocher « Remboursé ? ». Vous vous souvenez de l’explication ? La copie locale de l’enregistrement expense est déjà mise à jour ! Donc, pour l’instant au moins, lorsque le serveur nous indique qu’il a réussi à mettre à jour sa version, nous n’avons rien d’autre à faire.

Notez que ce code ne gère que le cas dans lequel le serveur réussit à mettre à jour l’enregistrement expense. Il faudra clairement que nous étudiions les cas où une erreur survient. Disons par exemple que la Comptabilité signale cette dépense comme non remboursable. Il devient alors impossible de régler ce champ sur true. Mais ce sera pour un autre jour.

Refactorisation des fonctions d’aide

Revenons à l’opportunité que nous avions identifiée de factoriser du code commun. Les deux fonctions d’aide sont identiques, à l’exception du rappel. Créons donc une nouvelle fonction plus générale qui accepte callback comme paramètre.

    saveExpense: function(component, expense, callback) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        if (callback) {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);
    },

Le paramètre callback est facultatif. S’il est présent, nous le passerons avec action. Simple. Et maintenant, nous pouvons réduire nos fonctions d’aide spécifiques à un événement au code suivant.

    createExpense: function(component, expense) {
        this.saveExpense(component, expense, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
    },
    updateExpense: function(component, expense) {
        this.saveExpense(component, expense);
    },

createExpense est seulement un peu plus court, mais il se focalise exclusivement sur ce qui doit être fait lorsque la réponse arrive (le rappel). Et waouh, updateExpense ne fait plus qu’une ligne !

Refactorisation du formulaire Add Expense

Ce petit exercice de refactorisation a été si gratifiant, et l’utilisation des événements si grisante, que nous allons recommencer, mais à plus grande échelle. Vers l’infini et au-delà !

Cette nouvelle tâche consiste à extraire le formulaire Add Expense du composant expenses et à le déplacer vers un nouveau composant qui lui est propre. L’extraction du balisage du formulaire est plutôt simple, il suffit de faire un copier/coller. Mais que faut-il déplacer d’autre ? Avant de commencer à déplacer des éléments dans tous les sens, réfléchissons à ce qui doit partir et ce qui doit rester.

Dans la conception actuelle, le gestionnaire d’actions du formulaire, clickCreate, gère la validation d’entrée, en envoyant la requête au serveur, et en mettant à jour l’état local et les éléments de l’interface utilisateur. Un gestionnaire d’actions sera toujours nécessaire pour le formulaire, et il devra probablement toujours gérer sa validation. Mais nous laisserons le reste en place, car nous gardons l’ensemble de notre logique de requête serveur dans le composant expenses.

Il va donc falloir détacher un peu (mais seulement un peu !) certains éléments des autres. Notre plan consiste à commencer par déplacer le balisage du formulaire, puis à effectuer le moins de déplacements possibles pour que tout fonctionne correctement. Nous allons refactoriser les deux composants de manière à ce qu’ils communiquent par événements plutôt que par accès direct à l’attribut de composant Tableau expenses.

Allons-y !

Dans le composant expenses principal, sélectionnez tout ce qui se trouve entre les deux commentaires <!-- CREATE NEW EXPENSE -->, y compris les commentaires de début et de fin eux-mêmes. Coupez la sélection pour la stocker dans votre presse-papiers (oui, coupez. C’est du sérieux).

Créez un composant Aura et nommez-le « expenseForm ». Collez le balisage du formulaire Add Expense dans le nouveau composant, entre les balises <aura:component>.

Revenez dans le composant expenses. Ajoutez le nouveau composant expenseForm au balisage. Cette section de expenses devrait ressembler à ce qui suit :

    <!-- NEW EXPENSE FORM -->
    <lightning:layout >
        <lightning:layoutItem padding="around-small" size="6">
            <c:expenseForm/>
        </lightning:layoutItem>
    </lightning:layout>

À ce stade, vous pouvez recharger votre application pour observer les modifications. Elles ne devraient pas être visibles. Mais, et ce n’est pas une surprise, le bouton Create Expense ne fonctionne plus.

Voyons rapidement les dernières choses à déplacer.

Maintenant, déplacez l’attribut newExpense du composant expenses vers le composant expenseForm. Cet attribut est utilisé pour les champs du formulaire, il doit donc se trouver dans le composant du formulaire. Aucune modification n’est nécessaire pour son déplacement. Il vous suffit donc de le couper dans un composant et de le coller dans l’autre.

Dans le composant expenseForm, créez les ressources de contrôleur et d’assistance.

Déplacez le gestionnaire d’actions clickCreate du contrôleur expenses vers le contrôleur expenseForm. Le bouton se trouve dans le composant du formulaire, son gestionnaire d’actions doit donc également s’y trouver. Ca alors, cette fois encore, aucune modification n’est nécessaire (vous commencez peut-être à comprendre le principe).

Il nous faut maintenant apporter quelques modifications. Mais vous ne serez pas trop dépaysé, puisqu’il s’agira simplement d’ajouter l’envoi d’événement, ce que nous avons déjà fait auparavant pour expenseItem. En outre, comme vous vous en souvenez, expenseItem envoie également un événement avec une charge utile expense, gérée par le composant expenses.

Dans l’Assistance expenseForm, créez la fonction createExpense.

    createExpense: function(component, newExpense) {
        let createEvent = component.getEvent("createExpense");
        createEvent.setParams({ "expense": newExpense });
        createEvent.fire();
    },

Elle semblera très similaire au gestionnaire d’actions clickReimbursed de expenseItem.

Si un composant est destiné à envoyer un événement, il doit l’inscrire. Ajoutez ce qui suit au balisage du composant expenseForm, immédiatement en dessous de l’attribut newExpense.

    <aura:registerEvent name="createExpense" type="c:expensesItemUpdate"/>

À ce stade, nous avons effectué tout le travail nécessaire pour implémenter le composant expenseForm. Vous devriez pouvoir recharger l’application, et le formulaire « fonctionnera », au sens où il n’y aura aucune erreur et où vous deviez voir les messages de formulaire appropriés lorsque vous entrerez des données non valides. Si vous utilisez Salesforce Lightning Inspector, vous pourrez même constater que l’événement expensesItemUpdate se déclenche. Tout ce qu’il reste à faire, c’est le gérer.

Avant que nous ne gérions l’événement, notez à quel point cette refactorisation était simple. La plupart du code n’a pas changé. Les étapes précédentes ont ajouté un total de six lignes et du balisage. Aujourd’hui ce travail paraît nouveau, mais une fois que vous l’aurez effectué à plusieurs reprises, vous réaliserez qu’il s’agit seulement de déplacer un peu de code.

Passons à la dernière étape. expenseForm déclenche l’événement createExpense, mais il est également nécessaire que le composant expenses s’en empare. Pour commencer, nous inscrivons le gestionnaire d’événement createExpense, et nous le lions au gestionnaire d’actions handleCreateExpense. Une fois encore, une seule ligne de balisage suffit. Ajoutez cette ligne immédiatement au-dessus ou en dessous du gestionnaire d’événement updateExpense.

    <aura:handler name="createExpense" event="c:expensesItemUpdate"
        action="{!c.handleCreateExpense}"/>

Enfin, pour la dernière étape, créez le gestionnaire d’actions handleCreateExpense dans le contrôleur expenses. Ajoutez ce code immédiatement au-dessus ou en dessous du gestionnaire d’actions handleUpdateExpense.

    handleCreateExpense: function(component, event, helper) {
        let newExpense = event.getParam("expense");
        helper.createExpense(component, newExpense);
    },

Oui, c’est aussi simple. Tout le travail est délégué à la fonction d’assistance createExpense, qui n’a pas bougé ni changé. Notre gestionnaire d’actions handleCreateExpense n’a plus qu’à relier les bons éléments ensemble.

Et cela conclut notre présentation du couplage léger de composants à l’aide d’événements. Créez et déclenchez l’événement dans un composant, et attrapez-le et gérez-le dans un autre. Le circuit sans fil est établi !

Leçon bonus — Petites améliorations visuelles

Avant d’entrer dans le vif du sujet, voici une modeste amélioration visuelle.

Nous souhaitons améliorer la présentation de notre application en ajoutant quelques composants de conteneur. Cela vous donnera aussi l’occasion d’observer le composant expense complet, avec toutes les modifications que nous lui avons apportées. Dans le composant expense, remplacez le balisage expensesList par le code ci-dessous.

<lightning:layout>
    <lightning:layoutItem padding="around-small" size="6">
        <c:expensesList expenses="{!v.expenses}"/>
    </lightning:layoutItem>
    <lightning:layoutItem padding="around-small" size="6">
        Put something cool here
    </lightning:layoutItem>
</lightning:layout>

Ces modifications ajoutent des marges et du remplissage pour rendre la liste des dépenses plus étroite. Cette mise en page laisse de la place pour ajouter du contenu à droite. Dans la prochaine unité, nous vous suggérerons deux exercices que vous pourrez effectuer seul.