Suivez votre progression
Accueil Trailhead
Accueil Trailhead

Saisie de données à l'aide de formulaires

Objectifs de formation

Une fois cette unité terminée, vous pourrez :
  • Créer un formulaire affichant les valeurs actuelles et acceptant de nouvelles entrées de l’utilisateur.
  • Lire des valeurs à partir des éléments du formulaire.
  • Valider les entrées de l’utilisateur et afficher des messages d’erreur en cas d’entrée non valide.
  • Refactoriser le code d’un contrôleur de composant dans son assistance.

Saisie de données à l'aide de formulaires

À partir de cette unité, nous en avons terminé avec les composants de style helloQuelqueChose. Nous allons maintenant créer et assembler la mini application de suivi des dépenses que nous avons entr’aperçue un peu plus tôt. Nous passerons la plus grande partie de cette unité à créer et à comprendre le formulaire qui vous permet de créer une nouvelle dépense.

Le conteneur d’application expenses

Avant de commencer à aborder ce sujet, occupons-nous plutôt de la création de composants basiques, voire franchement laids. La première chose que nous ferons, c’est invoquer le système Salesforce Lightning Design System, ou SLDS dans notre application. Ensuite, nous « l’activerons ». La manière dont nous le ferons permettra d’aborder un peu plus en détails les conteneurs d’applications.

Remarque

Remarque

Nous n’allons pas vraiment discuter du SLDS en tant que tel dans cette unité, ni même dans le reste dans ce module. Nous allons ici plutôt nous concentrer sur son ajout à une application, puis ensuite, dans notre exemple de code, nous utiliserons les classes SLDS, mais ne les expliquerons pas en détails. Reportez-vous aux Ressources pour obtenir une multitude de moyens différents d’en savoir plus sur le SLDS.

Actuellement, le SLDS est automatiquement accessible à vos composants lorsqu’ils sont exécutés dans Lightning Experience ou l’application Salesforce. Nous parlons parfois d’« exécution dans le conteneur one.app » pour désigner ce processus. Cette version intégrée est la même que celle utilisée par de nombreux composants Lightning standard. Cependant, SLDS n’est pas accessible par défaut dans une application autonome, ou lorsque vous utilisez vos composants hors de Lightning ou des composants Lightning pour Visualforce. Ce sont des conteneurs d’application différents et ils offrent des services et ressources variés. Nous aimerions créer notre application expense de manière à ce qu’elle fonctionne et s’affiche correctement dans tous ces contextes. Heureusement, ce n’est pas si difficile.

Nous le ferons en ajoutant SLDS à notre application tremplin. Ensuite, dans la « vraie » application expenses (le composant de haut niveau et tous ses composants descendants directs), nous pourrons utiliser les outils et techniques SLDS, sans nous soucier de la provenance des feuilles de styles de ressources SLDS, icônes SLDS, etc. C’est-à-dire que notre conteneur d’application (l’application tremplin) configure les ressources dans son contexte de manière à ce que toute application s’exécutant dans ce conteneur dispose des ressources dont elle a besoin.

Transformons donc ces longs discours en un peu de code. Créez une nouvelle application Lightning expensesApp.app comprenant le balisage suivant :

<aura:application extends="force:slds">
        <!-- This component is the real "app" -->
        <!-- c:expenses/ -->
</aura:application>

Voici ce qu’il se passe. L’attribut extends="force:slds" active SLDS dans cette application, en incluant les mêmes styles Lightning Design System que ceux fournis par Lightning Experience et l’application Salesforce. Mais notez que cette application tremplin n’est qu’une enveloppe, une coquille. La vraie application est le composant expenses, que nous n’avons pas encore créé (il s’agit de la partie <!-- c:expenses/ --> ; elle figure dans un commentaire car nous ne pouvons pas enregistrer notre application tant que le composant expenses n’existe pas réellement).

Grâce à l’application wrapper, nos composants utilisent le mécanisme extends="force:slds" pour accéder à SLDS lorsqu’ils s’exécutent à partir de cette application. Lorsqu’ils s’exécutent dans Lightning Experience ou l’application Salesforce, sans modification de code, ils utilisent cette inclusion automatique de SLDS du conteneur.

Dans le cas présent, les deux sont équivalents. Mais l’idée d’utiliser l’application tremplin externe pour établir un contexte de manière à ce que la vraie application n’ait pas besoin de se soucier des différences de contexte ne se limite pas aux ressources de style. Vous pouvez utiliser ce concept pour fournir des gestionnaires d’événement de remplacement, par exemple… mais n’allons pas trop vite. Apprenons à marcher avant d’essayer de voler !

Le composant d’application expenses

L’étape suivante consiste à créer un composant qui sera le niveau supérieur de notre application Expenses (souvenez-vous, même si nous parlons d’une « application », il s’agit en fait seulement d’un composant Lightning). Dans la Developer Console, créez un composant Aura appelé « expenses » et remplacez le balisage par défaut par ce qui suit.

<aura:component>
    <!-- PAGE HEADER -->
    <lightning:layout class="slds-page-header slds-page-header_object-home">
        <lightning:layoutItem>
            <lightning:icon iconName="standard:scan_card" alternativeText="My Expenses"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading_label">Expenses</h1>
                <h2 class="slds-text-heading_medium">My Expenses</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->
    <!-- NEW EXPENSE FORM -->
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="6">
        <!-- [[ expense form goes here ]] -->
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->
</aura:component>

Nous créons ici l’en-tête de la page en utilisant la présentation de grille fournie par les composants <lightning:layout> et <lightning:layoutItem>. size="6" crée un conteneur <div> qui représente 50 % de la largeur totale (ou la taille 6 sur 12). Comme vous l'avez peut-être remarqué, les composants lightning de l'espace de noms ressemblent aux composants de Lightning Experience et de l’application Salesforce. Outre les boutons et les présentations, vous trouverez dans cet espace de noms de nombreux autres composants pratiques parfaitement compatibles avec le style SLDS prêt à l’emploi.

Remarque

Remarque

Avez-vous remarqué la balise <lightning:icon> ? Ce composant restitue en un instant vos icônes SLDS favorites. Il n'est plus nécessaire de créer un composant helper pour afficher les icônes SLDS.

Vous pouvez maintenant retirer les commentaires autour de la balise <c:expenses/> dans le .app lui-même, et ouvrir l’aperçu de ce qui n’est, pour l’instant, qu’une coquille vide. Vous devez avoir un aperçu comme suit.

Formulaire basique Mes dépenses

Pour l’instant il ne se passe pas grand-chose, mais c’est réjouissant de voir que le style SLDS a déjà un effet. Souvenez-vous que nous n’allons pas expliquer la majeure partie du balisage SLDS, mais nous inclurons des commentaires à l’intérieur du balisage. Vous pouvez regarder comment nous avons créé l’en-tête de l’application pour commencer à comprendre l’idée.

Le formulaire New Expense

Avant de commencer le formulaire, nous avons un aveu à faire : ce que nous allons faire est temporaire. Vous vous rappelez de tout ce blabla concernant la décomposition de votre application en composants plus petits, et de sa création à partir de là ? Ce n’est pas ce que nous allons faire (pas encore), et honnêtement, c’est un peu de la triche.

Mais ça nous aidera car le code ne sera pas compliqué trop rapidement. Nous procédons ainsi afin de pouvoir nous concentrer sur une leçon à la fois. Et ce n’est pas une mauvaise chose, puisque cela vous permet d’étudier les choses vous-même : vous créez dans un composant jusqu’à ce qu’il devienne trop « plein », puis vous refactorisez et décomposez en sous-composants plus petits. Du moins, tant que nous n’oubliez pas de refactoriser !

Bien, passons à la suite. </sermon> Dans le composant expenses, remplacez le commentaire <!-- [[ expense form goes here ]] --> par le code suivant, correspondant au formulaire Add Expense :

    <!-- CREATE NEW EXPENSE -->
    <div aria-labelledby="newexpenseform">
        <!-- BOXED AREA -->
        <fieldset class="slds-box slds-theme_default slds-container_small">
        <legend id="newexpenseform" class="slds-text-heading_small
          slds-p-vertical_medium">
          Add Expense
        </legend>
        <!-- CREATE NEW EXPENSE FORM -->
        <form class="slds-form_stacked">
            <lightning:input aura:id="expenseform" label="Expense Name"
                             name="expensename"
                             value="{!v.newExpense.Name}"
                             required="true"/>
            <lightning:input type="number" aura:id="expenseform" label="Amount"
                             name="expenseamount"
                             min="0.1"
                             formatter="currency"
                             step="0.01"
                             value="{!v.newExpense.Amount__c}"
                             messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
            <lightning:input aura:id="expenseform" label="Client"
                             name="expenseclient"
                             value="{!v.newExpense.Client__c}"
                             placeholder="ABC Co."/>
            <lightning:input type="date" aura:id="expenseform" label="Expense Date"
                             name="expensedate"
                             value="{!v.newExpense.Date__c}"/>
            <lightning:input type="checkbox" aura:id="expenseform" label="Reimbursed?"
                             name="expreimbursed"
                             checked="{!v.newExpense.Reimbursed__c}"/>
            <lightning:button label="Create Expense"
                              class="slds-m-top_medium"
                              variant="brand"
                              onclick="{!c.clickCreate}"/>
        </form>
        <!-- / CREATE NEW EXPENSE FORM -->
      </fieldset>
      <!-- / BOXED AREA -->
    </div>
    <!-- / CREATE NEW EXPENSE -->

Cela paraît faire beaucoup de code à assimiler en une fois. Ce n'en est pas une. Lorsque vous retirez le balisage SLDS et les classes, ce formulaire se résume à une série de champs d'entrée avec un bouton pour la soumission du formulaire.

Voici le formulaire obtenu.

Formulaire New Expense
Remarque

Remarque

<lightning:input> est le couteau suisse des champs de saisie, empreint du style SLDS. Utilisez-le à chaque fois que vous auriez besoin d’employer une des nombreuses formes du composant <ui:input>, par exemple <ui:inputText>, <ui:inputNumber>, entre autres. Les composants de l'espace de noms ui ne contiennent pas de style SLDS et sont considérés comme des composants hérités.

Notez tout d’abord que nous créons plusieurs instances du composant <lightning:input> avec des types de données spécifiques. Ainsi, vous ne serez pas surpris d'utiliser type="date" avec un champ de date, etc. Il existe une variété de composants d’entrée, qui va bien au-delà des quatre présentés ici. Il est toujours préférable d’associer le type de composant au type de données. Si vous ne spécifiez pas de type, le type texte est appliqué par défaut. La raison ne vous semble peut-être pas encore évidente, mais elle le deviendra lorsque vous essaierez cette application sur un téléphone : les composants offrent des gadgets d’entrée parfaitement adaptés au format. Par exemple, le sélecteur de date est optimisé pour une entrée à la souris ou au doigt, selon l’appareil sur lequel vous y accédez.

Ensuite, remarquez qu’une étiquette est définie pour chaque composant, et que le texte de l’étiquette est automatiquement affiché à côté du champ d’entrée. Il y a quelques autres attributs que nous n’avons pas encore rencontrés auparavant : required, placeholder, type, min et step. La plupart de ces attributs sont similaires à leurs équivalents HTML. Par exemple, min spécifie la valeur minimale de l'entrée. Si vous ne réussissez pas à deviner à quoi ils font référence, consultez-les dans la bibliothèque de composants Lightning. (nous reviendrons à ce terme trompeur required).

Il y a aussi un attribut aura:id défini dans chaque balise. À quoi sert-il ? Il définit un ID unique (localement) pour chaque balise à laquelle il est ajouté, et cet ID permet d’extraire les valeurs des champs du formulaire. Dans cet exemple, les champs partagent tous le même ID. Par conséquent, nous pouvons y accéder en tant que groupe pour la validation des champs. Nous allons bientôt découvrir comment procéder.

Attributs des objets Salesforce (sObjects)

Mais nous devons d’abord nous intéresser à l’attribut value. Chaque balise a une valeur configurée comme une expression. Par exemple {!v.newExpense.Amount__c}. D’après le format de l’expression, vous pouvez déduire plusieurs éléments.

  • v signifie que c’est une propriété du fournisseur de valeur de la vue. Cela signifie qu’il s’agit d’un attribut du composant (que nous n’avons pas encore créé).
  • La notation pointée indique que newExpense est un type de données structuré. Cela signifie que newExpense a ses propres propriétés. Ou bien... des champs ?
  • Le « __c » final que l’on retrouve dans la plupart des noms de propriétés permet de deviner qu’elles renvoient à des champs personnalisés, figurant vraisemblablement dans l’objet personnalisé Expense.
  • Par conséquent, newExpense est sans doute un objet de type Expense !

Super, on n’en a pas encore parlé ! Voici la véritable définition d’attribut que vous devez ajouter en haut du composant, juste après la balise d’ouverture <aura:component>.

    <aura:attribute name="newExpense" type="Expense__c"
         default="{ 'sobjectType': 'Expense__c',
                        'Name': '',
                        'Amount__c': 0,
                        'Client__c': '',
                        'Date__c': '',
                        'Reimbursed__c': false }"/>

Ce qu’elle fait est assez simple. Vous connaissez déjà l’attribut name. Et pas de surprise, le type correspond au nom de l’API de notre objet personnalisé. Jusqu’ici, tout va bien.

L’attribut par défaut n’est pas nouveau, à la différence du format de sa valeur. Mais ça ne devrait pas être trop compliqué à comprendre. Il s’agit d’une représentation JSON d’un sObject, qui spécifie le type d’objet (le nom de l’API) et les valeurs de chacun des champs qui lui sont attribués par défaut. Dans ce cas, nous configurons tout pour représenter une valeur vide.

Et c’est à peu près tout ce que vous devez savoir sur les sObjects ! À partir de ce point, le framework des composants Lightning vous laisse traiter newExpense, en JavaScript et dans son balisage, comme s’il s’agissait d’un enregistrement Salesforce, même si nous ne le chargeons pas (encore) depuis Salesforce !

Traitement de la soumission d’un formulaire avec un gestionnaire d’actions

Nous avons donc un formulaire. Si vous le remplissez et que vous cliquez sur le bouton pour créer une nouvelle dépense, que se passera-t-il ? Si vous ne l’avez pas créé tout seul, vous obtiendrez une erreur signalant l’absence d’une action de contrôleur. En effet, ni le contrôleur, ni le gestionnaire d’actions spécifiés dans <lightning:button> n’ont encore été créés.

Dans la Developper Console, cliquez sur le bouton CONTROLLER du composant expenses pour créer la ressource contrôleur. Puis remplacez le code par défaut par :

({
    clickCreate: function(component, event, helper) {
        let validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            let newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})

Tout est nouveau donc nous allons tout décortiquer. D’abord, remarquons que cette fonction de gestionnaire d’actions est divisée en trois parties ou étapes :

  1. Configuration
  2. Traitement des valeurs du formulaire
  3. S’il n’y a pas d’erreurs, faire quelque chose

Cette structure vous est peut-être familière car c’est une méthode fondamentale de traitement des entrées utilisateurs dans une application Web. Examinons chaque étape pour observer son fonctionnement dans le composant Lightning.

Pour la configuration, nous nous contentons d’initialiser l’état de notre vérification d’erreurs. C’est un choix simple : s’agit-il d’une dépense valide ? À chaque appel du gestionnaire d’action clickCreate, nous considérons d'abord que les données de dépense sont correctes, puis nous les invalidons en cas de problème. Voici un aperçu de l'indicateur validExpense, avec une valeur initiale définie sur true.

  • component.find('expenseform') récupère une référence au tableau des champs <lightning:input> qui doivent être validés. Si l'ID est unique, la référence renvoie le composant. Dans le cas présent, l'ID n'est pas unique et la référence renvoie un tableau de composants.
  • La méthode JavaScript reduce() réduit le tableau à une valeur unique qui est capturée par validSoFar, et reste true jusqu'à la détection d'un champ non valide, modifiant validSoFar sur false. Un champ non valide peut être par exemple un champ obligatoire laissé vide, un champ contenant un nombre inférieur à la valeur minimale.
  • inputCmp.get('v.validity').valid renvoie la validité du champ de saisie actuel dans le tableau.
  • inputCmp.showHelpMessageIfInvalid() affiche un message d’erreur lorsque des champs ne sont pas valides. <lightning:input> fournit des messages d’erreur par défaut qui peuvent être personnalisés par des attributs tels que messageWhenRangeUnderflow, que vous avez vu dans l’exemple du formulaire de dépenses.

Explorons quelques détails intéressants. Dans helloMessageInteractive, nous n’utilisions pas find() pour obtenir le texte de l’étiquette du bouton qui avait été cliqué. Ce n’était pas nécessaire. Nous pouvions obtenir directement une référence à ce bouton, en la récupérant à partir du paramètre event grâce à event.getSource(). Mais c’est un luxe que vous n’aurez pas toujours. En fait, il est plutôt rare que tout ce dont vous ayez besoin dans une entrée utilisateur ne provienne que de l’événement.

Donc, quand votre contrôleur a besoin d’une manière d’obtenir un composant enfant, commencez par donner un aura:id à ce composant dans ses balises, puis utilisez component.find(theId) pour obtenir une référence à ce composant lors de l’exécution.

Remarque

Remarque

component.find() vous permet uniquement d’accéder au composant et à ses composants enfants à partir du contrôleur et de l’assistance. Ce n’est pas une manière magique de se promener dans la hiérarchie des composants pour lire ou modifier des choses. Souvenez-vous : les composants sont censés être autosuffisants, ou pouvoir communiquer avec... On en parlera plus tard.

La validation avec <lightning:input> exploite la puissance de l’élément d’entrée HTML sous-jacent afin de traiter dans la plupart des cas les valeurs de formulaire à votre place. Vous devez valider un numéro de téléphone ? Utilisez type="tel", puis définissez l'attribut pattern en utilisant une expression régulière. Vous devez valider une valeur de pourcentage ? Utilisez type="number" avec formatter="percent". Vous devez valider un autre élément ? Laissez <lightning:input> le valider pour vous.

C’est quand la validation échoue que les choses redeviennent intéressantes. Si l’entrée de l’utilisateur n’est pas valide, nous voulons que deux choses se produisent.

  1. Ne pas essayer de créer la dépense.
  2. Afficher un message d’erreur constructif.

Pour le premier, nous définissons l'indicateur validExpense sur false, lorsque la validité du champ référencé par inputCmp.get('v.validity').valid renvoie false. Pour le deuxième, nous utilisons les erreurs de validation intégrées ou nous fournissons des messages d'erreur personnalisés. Dans le formulaire de dépense, le champ de nom obligatoire indique « Renseignez ce champ » s'il est vide lorsque vous essayez de soumettre le formulaire. Vous pouvez toutefois fournir votre propre message personnalisé en spécifiant messageWhenValueMissing="Vous m'avez oublié ?"

Par contre, si le champ est validé, l'indicateur validExpense est évalué true et aucune erreur n'est affichée.

Et nous arrivons donc à la troisième étape du traitement de la soumission du formulaire : créer la dépense ! Comme vous pouvez le voir, pour nous y préparer, nous récupérons l’objet newExpense complet à partir de l’attribut du composant : component.get("v.newExpense"). Cela nous donne une variable unique que nous pouvons utiliser pour créer un nouvel enregistrement de dépense.

Mais avant de le faire, voici une question pour vous : pourquoi ne pas récupérer les valeurs du formulaire depuis newExpense ? Pourquoi ne pas récupérer la variable structurée une fois, au début du gestionnaire d’actions, puis accéder à ses propriétés, au lieu d’une longue série d’appels find().get() ?

La raison est simple : les références aux champs individuels doivent pouvoir appeler showHelpMessageIfInvalid() sur les champs. De plus c’est une bonne pratique de valider les données brutes des formulaires ; votre logique de validation ne sait pas quel type de traitement pourra concerner l’objet newExpense.

Création d’une nouvelle dépense

Vous vous souvenez que nous avons dit plus tôt que c’était un peu de la triche d’insérer le formulaire expense dans le composant principal ? Eh bien, cette section n’a même pas droit au « un peu ». Ici, nous allons tout simplement contourner la difficulté de créer d’un véritable enregistrement. Nous l’évitons pour le moment puisque cela constitue le sujet de la prochaine unité. Donc pour le moment, il faudra nous contenter de quelque chose de simple, qui nous montrera tout de même certains concepts importants.

D’abord, créons un endroit où « stocker » les nouvelles dépenses. Nous nous contenterons de créer un tableau de dépenses uniquement local pour les stocker. En haut du balisage du composant expenses, immédiatement avant l’attribut newExpense, ajoutez un nouvel attribut expenses, qui accueillera un tableau d’objets expense.

    <aura:attribute name="expenses" type="Expense__c[]"/>

Tout ce que nous avons à faire, c’est mettre à jour le tableau expenses. Cette opération s’avérera simple (dans la version tricheur de notre formulaire) et illustrera un autre concept important.

Dans notre contrôleur, nous avons dissimulé le véritable travail de création de la nouvelle dépense derrière cet appel de fonction : helper.createExpense(component, newExpense). En développement logiciel, un autre mot pour « dissimulation » est abstraction. Et nous utiliserons une fonction d’assistance pour faire abstraction de notre tricherie.

Nous avons brièvement évoqué les fonctions d’assistance auparavant, mais nous n’entrerons pas dans le détail dans ce module. Pour le moment, disons trois choses sur les assistances :

  • Une assistance de composant est le bon endroit où placer le code à partager entre plusieurs gestionnaires d’actions différents.
  • Une assistance de composant est un excellent endroit où placer les détails de traitement complexes, afin que la logique de vos gestionnaires d’actions reste simple et claire.
  • Les fonctions d’assistance peuvent avoir n’importe quelle signature de fonction. C’est-à-dire qu’elles ne sont pas contraintes par les gestionnaires d’actions du contrôleur (pourquoi cela ? Car vous appelez la fonction d’assistance directement à partir de votre code. Au contraire, le framework appelle les gestionnaires d’actions via son exécution). Les bonnes pratiques recommandent de toujours fournir le composant comme premier paramètre des fonctions d’assistance.

OK, allons-y. Dans la Developer Console, cliquez sur le bouton HELPER du composant expenses afin de créer la ressource d’assistance associée, puis remplacez l’exemple de code par le suivant.

({
    createExpense: function(component, expense) {
        let theExpenses = component.get("v.expenses");
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        let newExpense = JSON.parse(JSON.stringify(expense));
        theExpenses.push(newExpense);
        component.set("v.expenses", theExpenses);
    }
})

Pour le moment, ignorez la partie « bidouillage dégoûtant ». Les trois autres lignes de code reprennent un schéma courant que nous avons déjà rencontré auparavant, et que vous utiliserez encore et encore : obtenir-traiter-régler. Pour commencer, nous obtenons le tableau de dépenses de l’attribut expenses. Ensuite, nous lui ajoutons le nouvel « enregistrement » de dépense. Enfin, nous mettons à jour (c’est-à-dire que nous le paramétrons avec set) l’attribut expenses avec le tableau modifié.

La référence n’est pas la collection

Ce qui est nouveau ici, c’est que pour la première fois, nous mettons à jour une collection, un tableau. Et si vous êtes programmeur expérimenté, vous vous demandez probablement : « Pourquoi aurais-je besoin de set() ici ? »

En fait, component.get("v.expenses") obtient une référence au tableau stocké dans l’attribut component. component.set("v.expenses", theExpenses) attribue simplement la même référence à l’attribut component. Bien sûr, entre temps, le contenu du tableau a été ajouté, mais le conteneur est le même : la référence au tableau n’a pas changé ! Alors, pourquoi la mettre à jour ?

Si vous avez des difficultés à comprendre ce que cela signifie, ajoutez deux instructions de journalisation avant et après les instructions critiques, et placez le contenu de theExpenses dans la console.

console.log("Expenses before 'create': " + JSON.stringify(theExpenses));
theExpenses.push(newExpense);
component.set("v.expenses", theExpenses);
console.log("Expenses after 'create': " + JSON.stringify(theExpenses));

Rechargez et exécutez l’application, ajoutez au moins deux dépenses, puis examinez la structure de theExpenses. Maintenant, commentez la ligne component.set() et refaites la même opération.

Mais que se passe-t-il donc ? component.set() n’a absolument aucune influence sur theExpenses ! Mais ! Mais ! Mais ? Mais que fait-il vraiment ?

Vous avez absolument raison de vous poser cette question. Et la réponse est : c’est magique !

Ici, component.set() ne met pas à jour la valeur de l’attribut expenses. Il déclenche une notification indiquant que l’attribut expenses a changé.

L’effet est le suivant : partout où vous avez référencé l’attribut expenses dans une expression, la valeur de cette expression est mise à jour, et cette mise à jour est en cascade, elle s’applique partout, à chaque apparition de l’attribut expenses. Toutes ces occurrences sont mises à jour conformément au nouveau contenu. Tout se produit en coulisse, grâce au framework de composants Lightning, dans le cadre de l’auto-connexion établie lorsque vous utilisez {!v.expenses}. En un mot, c’est magique.

Pour résumer, s’il s’agissait de JavaScript brut, vous n’auriez pas besoin de component.set(). Par contre, vous en auriez besoin pour déclencher les effets sous-jacents intégrés au modèle de programmation de composants Aura. Si vous avez déjà écrit du code de contrôleur ou helper, testez-le : il ne se passe rien. Veillez à utiliser la bonne balise component.set().

Le « bidouillage dégoûtant » permet de contourner un autre problème de références similaire. Pour observer ce problème, modifiez la ligne en supprimant les deux appels JSON, puis testez l’application. Vous verrez rapidement le problème. Nous allons l’éliminer dans la prochaine unité, donc ne l’expliquerons pas davantage ici.

Affichage de la Liste des dépenses

On a beaucoup parlé de mettre à jour « magiquement » tout ce qui utilise {!v.expenses}, mais devinez quoi. Pour l’instant, il n’est utilisé nulle part ailleurs. Corrigeons ce défaut.

Dans la Developer Console, créez un composant Aura appelé expenseItem et remplacez le balisage par défaut par ce qui suit. Si vous avez déjà créé expenseItem, mettez seulement le balisage à jour. Vous avez vu précédemment les expressions qui accèdent aux champs dans l'enregistrement de dépenses. Cette version inclut un balisage SLDS qui permet d'améliorer son style.
<aura:component>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="formatdate" type="Date"/>
    <aura:attribute name="expense" type="Expense__c"/>
    <lightning:card title="{!v.expense.Name}" iconName="standard:scan_card"
                    class="{!v.expense.Reimbursed__c ?
                           'slds-theme_success' : ''}">
        <aura:set attribute="footer">
            <p>Date: <lightning:formattedDateTime value="{!v.formatdate}"/></p>
            <p class="slds-text-title"><lightning:relativeDateTime value="{!v.formatdate}"/></p>
        </aura:set>
        <p class="slds-text-heading_medium slds-p-horizontal_small">
           Amount: <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
        </p>
        <p class="slds-p-horizontal_small">
            Client: {!v.expense.Client__c}
        </p>
        <p>
            <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}"/>
        </p>
    </lightning:card>
</aura:component>
Notez que <lightning:card> attribue un thème SLDS lorsque le champ de l’élément de dépense Remboursé ? est coché : {!v.expense.Reimbursed__c ? 'slds-theme_success' : ''}. Cette expression vous permet de contrôler l’apparence des éléments de dépense remboursés dans l’interface utilisateur. Sans autre personnalisation, les éléments de dépense apparaissent dans le style du composant expensesList contenant les éléments de dépense. Pour personnaliser le composant expenseItem, cliquez sur STYLE et ajoutez les éléments suivants.
.THIS.slds-card.slds-theme_success {
    background-color: rgb(75, 202, 129);
}

Créez ensuite un contrôleur côté client expenseItemController.js avec le code ci-dessous. Ici, nous convertissons la date renvoyée ultérieurement par le serveur en un objet Date JavaScript afin de permettre à <lightning:formattedDateTime> et à <lightning:relativeDateTime> de l’afficher correctement. Cette conversion est gérée durant l’initialisation du composant, capturée par la balise <aura:handler>. Il est pratique pour traiter l'événement d'initialisation. Nous le réutilisons plus loin pour charger les données Salesforce.

({
    doInit : function(component, event, helper) {
        let mydate = component.get("v.expense.Date__c");
        if(mydate){
            component.set("v.formatdate", new Date(mydate));
        }
    },
})
Dans la Developer Console, créez un composant Aura appelé expensesList et remplacez le balisage par défaut par ce qui suit.
<aura:component>
    <aura:attribute name="expenses" type="Expense__c[]"/>
    <lightning:card title="Expenses">
        <p class="slds-p-horizontal_small">
            <aura:iteration items="{!v.expenses}" var="expense">
                <c:expenseItem expense="{!expense}"/>
            </aura:iteration>
        </p>
    </lightning:card>
</aura:component>

Pas beaucoup de nouveautés. C’est un composant qui affiche une liste de dépenses. Il dispose d’un attribut, expenses, qui consiste en un tableau d’objets de dépense de type expense (Expense__c). En outre, il utilise un composant <aura:iteration> pour créer un <c:expenseItem> pour chacun de ces objets de dépense. L’effet global est, comme on l’a dit, d’afficher une liste de dépenses. Jusqu’ici, tout va bien.

Maintenant, ajoutez le composant expensesList à la fin du composant expenses. Ajoutez-le juste avant la balise de fin </aura:component> dans expenses.cmp.

<c:expensesList expenses="{!v.expenses}"/>

Si vous rechargez l’application, vous verrez une section Expenses sous le formulaire (visuellement, ce n’est pas parfait, mais pour l’instant, ça ira).

Qu’est-ce que l’on vient de faire ? Nous avons ajouté le composant expensesList à la fin du composant expenses et lui avons transmis l’attribut expenses principal. Donc maintenant, l’instance de expenses qu’a expensesList est la même instance de expenses que celle du composant expenses. Elles sont des références au même tableau d’enregistrements, et grâce à la magie des composants Lightning, lorsque le tableau expenses principal est mis à jour, le composant expensesList le « remarquera » et affichera la liste mise à jour. Essayez !

Ouf ! C’était une longue unité, il faut faire une pause. Levez-vous et marchez un peu pendant quelques minutes.

Puis revenez et nous vous apprendrons comment enregistrer réellement vos nouvelles dépenses.