Suivez votre progression
Accueil Trailhead
Accueil Trailhead

Manipulation des enregistrements avec force:recordData

Objectifs de formation

Une fois cette unité terminée, vous pourrez :

  • Utiliser Lightning Data Service pour les composants Aura afin de créer, lire, charger et supprimer des enregistrements.
  • Construire un composant qui utilise force:recordData.
  • Expliquer comment Lightning Data Service met les enregistrements en cache

Que force:recordData soit avec vous

L’unité précédente nous a permis de découvrir les améliorations de performances sympathiques et les fonctionnalités utiles de Lightning Data Service. Nous allons maintenant apprendre à les utiliser.

Souvenez-vous, en tant que tel force:recordData n’inclut pas d’éléments d’interface. La balise force:recordData correspond uniquement à la logique utilisée pour communiquer avec le serveur et gérer le cache local. Pour que vos utilisateurs puissent voir et modifier les données récupérées par LDS, vous devrez inclure des éléments d’interface. La balise force:recordData utilise l’API de l’interface utilisateur pour fournir des données à vos composants d’interface.

Lorsque vous utilisez force:recordData, chargez les données une première fois, puis transmettez-les ensuite aux composants enfants en tant qu’attributs. Cette approche réduit le nombre de modules d’écoute et minimise la quantité d’appels au serveur, ce qui améliore les performances et garantit que vos composants affichent des données cohérentes.

Chargement des enregistrements

Pour mettre un enregistrement à disposition de vos composants d’interface, vous devez d’abord le charger. Pour charger l’enregistrement, vous devez inclure force:recordData dans votre composant en spécifiant les attributs recordId, mode, et layoutType ou fields.

ldsDisplayRecord.cmp
<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId"> <!--inherit recordId attribute-->

<aura:attribute name="record" type="Object" 
  description="The record object to be displayed"/>
<aura:attribute name="simpleRecord" type="Object" 
  description="A simplified view record object to be displayed"/>
<aura:attribute name="recordError" type="String" 
  description="An error message bound to force:recordData"/>

<force:recordData aura:id="record"
    layoutType="FULL"
    recordId="{!v.recordId}"
    targetError="{!v.recordError}"
    targetRecord="{!v.record}"
    targetFields ="{!v.simpleRecord}"
    mode="VIEW"/>

Ajoutez ensuite le balisage qui affiche les données chargées par force:recordData.

<!-- Display a lightning card with details about the record -->
<lightning:card iconName="standard:account" title="{!v.simpleRecord.Name}" >
    <div class="slds-p-horizontal--small">
        <p class="slds-text-heading--small">
            <lightning:formattedText title="Billing City" value="{!v.simpleRecord.BillingCity}" /></p>
        <p class="slds-text-heading--small">
            <lightning:formattedText title="Billing State" value="{!v.simpleRecord.BillingState}" /></p>
    </div>
</lightning:card>

<!-- Display Lightning Data Service errors, if any -->
<aura:if isTrue="{!not(empty(v.recordError))}">
    <div class="recordError">
        {!v.recordError}
    </div>
</aura:if>

</aura:component>

Étant donné que le composant personnalisé ldsDisplayRecord met en œuvre flexipage:availableForRecordHome et force:hasRecordId, vous pouvez ajouter le composant sur une page d’enregistrement à l’aide du générateur d’applications Lightning en suivant les étapes décrites dans Création et configuration de pages d’enregistrement Lightning Experience. Voici à quoi ressemble ldsDisplayRecord dans le générateur d’applications Lightning.

Le composant ldsDisplayRecord dans le générateur d’applications Lightning

L’interface flexipage:availableForRecordHome rend le composant personnalisé disponible dans le générateur d’applications Lightning pour une utilisation sur les pages d’enregistrement. L’interface force:hasRecordId fournit l’ID d’enregistrement au composant personnalisé pour la page d’enregistrement contenant le composant. Vous pouvez l’utiliser pour créer des interfaces utilisateur personnalisées avec les données d’enregistrement fournies par Lightning Data Service.

Sauvegarde des enregistrements

La magie de LDS opère quand plusieurs composants d’une application Lightning appellent le même enregistrement de données. Certains composants affichent simplement les données de l’enregistrement, mais d’autres peuvent les manipuler. Ce composant charge l’enregistrement avec un petit formulaire qui permet à l’utilisateur de lui donner un nouveau nom.

ldsSaveRecord.cmp
<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId"> <!--inherit recordId attribute-->

<aura:attribute name="record" type="Object" />
<aura:attribute name="simpleRecord" type="Object" />
<aura:attribute name="recordError" type="String" />

<force:recordData aura:id="recordEditor"
    layoutType="FULL"
    recordId="{!v.recordId}"
    targetError="{!v.recordError}"
    targetRecord="{!v.record}"
    targetFields ="{!v.simpleRecord}"
    mode="EDIT" />

    <!-- Display a lightning card with details about the record -->

    <lightning:card iconName="standard:account" title="{!v.simpleRecord.Name}" >
        <div class="slds-p-horizontal--small">
            <p class="slds-text-heading--small">
                <lightning:formattedText title="Billing State" value="{!v.simpleRecord.BillingState}" /></p>
            <p class="slds-text-heading--small">
                 <lightning:formattedText title="Billing City" value="{!v.simpleRecord.BillingCity}" /></p>
        </div>
    </lightning:card>
    <br/>

    <!-- Display an editing form -->
    <lightning:card iconName="action:edit" title="Edit Account">
        <div class="slds-p-horizontal--small">
            <lightning:input label="Account Name" value="{!v.simpleRecord.Name}"/>
            <br/>
            <lightning:button label="Save Account" variant="brand" onclick="{!c.handleSaveRecord}" />
        </div>
    </lightning:card>

    <!-- Display Lightning Data Service errors, if any -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}
        </div>
    </aura:if>

</aura:component>

Pour traiter cette mise à jour, créez un contrôleur JavaScript qui appelle la méthode saveRecord(). La méthode saveRecord() prend comme seul paramètre une fonction de rappel, SaveRecordResult. SaveRecordResult inclut un attribut state qui vous dira si l’enregistrement a réussi, et vous donnera d’autres informations que vous pourrez utiliser pour traiter le résultat de l’opération.

LdsSaveRecordController.js
({
    handleSaveRecord: function(component, event, helper) {
        component.find("recordEditor").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                console.log("Save completed successfully.");
            } else if (saveResult.state === "INCOMPLETE") {
                console.log("User is offline, device doesn't support drafts.");
            } else if (saveResult.state === "ERROR") {
                console.log('Problem saving record, error: ' + 
                           JSON.stringify(saveResult.error));
            } else {
                console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
            }
        }));}
})

Pas mal, hein ? LDS fait le plus gros du travail en coulisses : il envoie la requête au serveur et met à jour automatiquement les deux enregistrements.

Le composant ldsSaveRecord est semblable au composant ldsDisplayRecord et peut être ajouté à une page d’enregistrement à l’aide du générateur d’applications Lightning.

Passons maintenant au reste du CRUD.

Création d’enregistrements

Pour créer un enregistrement vide, laissez l’attribut recordId de force:recordData indéfini.

ldsNewRecord.cmp
<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId">

<aura:attribute name="newContact" type="Object"/>
<aura:attribute name="simpleNewContact" type="Object"/>
<aura:attribute name="newContactError" type="String"/>

<force:recordData aura:id="contactRecordCreator"
    layoutType="FULL"
    targetRecord="{!v.newContact}"
    targetFields ="{!v.simpleNewContact}"
    targetError="{!v.newContactError}"
    />

<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <!-- Display the new contact form -->
    <lightning:card iconName="action:new_contact" title="Create Contact">
        <div class="slds-p-horizontal--small">
            <lightning:input aura:id="contactField" label="First Name" value="{!v.simpleNewContact.FirstName}"/>
            <lightning:input aura:id="contactField" label="Last Name" value="{!v.simpleNewContact.LastName}"/>
            <lightning:input aura:id="contactField" label="Title" value="{!v.simpleNewContact.Title}"/>
            <br/>
            <lightning:button label="Save Contact" variant="brand" onclick="{!c.handleSaveContact}"/>
        </div>
    </lightning:card>

    <!-- Display Lightning Data Service errors -->
    <aura:if isTrue="{!not(empty(v.newContactError))}">
        <div class="recordError">
            {!v.newContactError}
        </div>
    </aura:if>

</aura:component>
Dans le contrôleur du composant, appelez la méthode getNewRecord(). Quand l’utilisateur crée un enregistrement, utilisez la méthode saveRecord() présentée plus haut pour l’enregistrer.
Remarque

Remarque

Maintenant, pourquoi ne pas utiliser un autre élément qu'un champ de texte, par exemple une liste de sélection, une case à cocher ou une case d'option ? Avant tout, nous recommandons d'utiliser des composants basés sur un formulaire, présentés dans l'unité précédente, qui offrent un mappage de champs prêt à l'emploi, ainsi que la présentation, la validation, les modifications CRUD (créer, lire, mettre à jour et supprimer) et le traitement des erreurs. Avec force:recordData, vous devez raccorder vos composants aux champs Salesforce que vous souhaitez utiliser.

ldsNewRecordController.js
({
    doInit: function(component, event, helper) {
        // Prepare a new record from template
        component.find("contactRecordCreator").getNewRecord(
            "Contact", // sObject type (entityAPIName)
            null,      // recordTypeId
            false,     // skip cache?
            $A.getCallback(function() {
                var rec = component.get("v.newContact");
                var error = component.get("v.newContactError");
                if(error || (rec === null)) {
                    console.log("Error initializing record template: " + error);
                }
                else {
                    console.log("Record template initialized: " + rec.apiName);
                }
            })
        );
    },

    handleSaveContact: function(component, event, helper) {
        if(helper.validateContactForm(component)) {
            component.set("v.simpleNewContact.AccountId", component.get("v.recordId"));
            component.find("contactRecordCreator").saveRecord(function(saveResult) {
                if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                    // record is saved successfully
                    var resultsToast = $A.get("e.force:showToast");
                    resultsToast.setParams({
                        "title": "Saved",
                        "message": "The record was saved."
                    });
                    resultsToast.fire();

                } else if (saveResult.state === "INCOMPLETE") {
                    // handle the incomplete state
                    console.log("User is offline, device doesn't support drafts.");
                } else if (saveResult.state === "ERROR") {
                    // handle the error state
                    console.log('Problem saving contact, error: ' + 
                                 JSON.stringify(saveResult.error));
                } else {
                    console.log('Unknown problem, state: ' + saveResult.state +
                                ', error: ' + JSON.stringify(saveResult.error));
                }
            });
        }
    }
})

Le composant personnalisé ldsNewRecord crée un nouvel enregistrement de contact et le champ Nom du compte (AccountId) est nécessaire à un enregistrement de contact. Supposons que vous ajoutez le composant personnalisé ldsNewRecord à une page d’enregistrement de compte. L’attribut v.recordId renvoie alors l’ID d’enregistrement de la page d’enregistrement de compte où ldsNewRecord se trouve. La ligne component.set("v.simpleNewContact.AccountId", component.get("v.recordId")); définit l’ID d’enregistrement de compte dans le champ AccountId.

Ce helper valide les valeurs de formulaire.

ldsNewRecordHelper.js
({
    validateContactForm: function(component) {
        var validContact = true;

         // Show error messages if required fields are blank
        var allValid = component.find('contactField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
            // Verify we have an account to attach it to
            var account = component.get("v.newContact");
            if($A.util.isEmpty(account)) {
                validContact = false;
                console.log("Quick action context doesn't have a valid account.");
            }
        return(validContact);
            
        }  
	}
       
})

Suppression d’enregistrements

Enfin, pour supprimer un enregistrement, précisez au moins le recordId en définissant « Id » comme attribut des champs.

ldsDeleteRecord.cmp
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">

<aura:attribute name="recordError" type="String" access="private"/>

<force:recordData aura:id="recordHandler"
    recordId="{!v.recordId}"
    fields="Id"
    targetError="{!v.recordError}"
    />

    <!-- Display the delete record form -->
    <lightning:card iconName="action:delete" title="Delete Record">
        <div class="slds-p-horizontal--small">
            <lightning:button label="Delete Record" variant="destructive" onclick="{!c.handleDeleteRecord}"/>
        </div>
    </lightning:card>

    <!-- Display Lightning Data Service errors, if any -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}
        </div>
    </aura:if>

</aura:component>

Dans le contrôleur JavaScript du composant, appelez la méthode deleteRecord(). LDS supprime l’enregistrement du cache et déclenche une notification. La méthode deleteRecord() accepte une fonction de rappel, comme la méthode saveRecord(). Celle-ci s’appelle deleteRecordResult et indique si l’opération a réussi.

ldsDeleteRecordController.js
({
    handleDeleteRecord: function(component, event, helper) {
    component.find("recordHandler").deleteRecord($A.getCallback(function(deleteResult) {
        if (deleteResult.state === "SUCCESS" || deleteResult.state === "DRAFT") {
            console.log("Record is deleted.");
            var resultsToast = $A.get("e.force:showToast");
            resultsToast.setParams({
                "title": "Deleted",
                "message": "The record was deleted."
            });
            resultsToast.fire();
        }
        else if (deleteResult.state === "INCOMPLETE") {
            console.log("User is offline, device doesn't support drafts.");
        }
        else if (deleteResult.state === "ERROR") {
            console.log('Problem deleting record, error: ' +
                        JSON.stringify(deleteResult.error));
        }
        else {
            console.log('Unknown problem, state: ' + deleteResult.state +
                        ', error: ' + JSON.stringify(deleteResult.error));
        }
    }));
}})

Sauvegarde asynchrone d’un enregistrement

Imaginez un peu. Vous utilisez l’application Salesforce, et une tentative de sauvegarde ne parvient pas au serveur en raison de problèmes de connexion. Peut-être que votre train est passé dans un tunnel, que vous vous êtes retrouvé dans un endroit qui ne capte pas, ou que des lutins ont encore bricolé les antennes téléphoniques. Dans tous les cas, pas de panique, LDS s’occupe de tout. En cas de problème de connexion, Lightning Data Service enregistre vos modifications dans un cache local. C’est indiqué par l’état DRAFT dans l’objet SaveRecordResult. L’état DRAFT d’un enregistrement est levé lorsque la connexion est rétablie. Lorsque vous enregistrez un enregistrement en utilisant LDS, le cache local n’est pas modifié tant que l’enregistrement n’a pas réussi. Lorsque l’enregistrement réussit sur le serveur, le cache est mis à jour avec la dernière version de l’enregistrement depuis le serveur, et tous les composants avec une référence à cet enregistrement sont notifiés. Vous n’avez pas à recharger manuellement l’enregistrement dans le cache après l’enregistrement. LDS s’en occupe pour vous.

Les enregistrements qui ont lieu lorsqu’un appareil est hors ligne entraînent un état DRAFT si vous avez activé les permissions de sauvegarde asynchrone, ou si toutes les conditions suivantes sont vraies :
  • Le client n’a pas réussi à joindre le serveur.
  • L’organisation autorise les brouillons hors ligne.
  • Vous utilisez la version 9.0 ou plus de l'application Salesforce.

Toutes les opérations CRUD tentent de se résoudre immédiatement par une requête XMLHttpRequest vers le serveur. Si votre appareil perd la connexion avec le serveur, Lightning Data Service peut obtenir des données après d’un cache local. LDS récupère les données dans le cache local ou depuis le serveur en fonction de l’âge de l’enregistrement, et de l’existence d’un brouillon local. Si un enregistrement est suffisamment récent, ou si un brouillon local existe, LDS utilise le cache local. LDS ne rafraîchit les enregistrements que lorsque nécessaire. Tous les rafraîchissements sont donc déclenchés par un composant.

Création d'une organisation Trailhead Playground

Bonne nouvelle ! Vous pouvez vous entraîner à utiliser LDS dans un Trailhead Playground (TP) gratuit. Qu’est-ce qu’un TP ? C’est une organisation Salesforce Developer Edition spécialement prévue pour être utilisée avec Trailhead. Vous pouvez lancer un TP (ou en créer un nouveau) depuis n’importe quel défi pratique. Créez donc un nouveau TP tout de suite. (Utiliser une organisation existante peut poser problème lors de la vérification du défi.) Accédez au bas de cette page. Cliquez sur la flèche à côté de Lancer, puis sélectionnez Créer un Trailhead Playground (connexion requise). Si jamais vous aviez besoin des identifiants de votre nouveau TP, suivez les instructions de cet article.