Suivez votre progression
Accueil Trailhead
Accueil Trailhead

Installation de l'exemple d'application

Objectifs de formation

Une fois cette unité terminée, vous pourrez :
  • Installer l'application exemple Record Viewer.
  • Comprendre la structure de l'application Record Viewer.
  • Examiner un enregistrement dans l'application Record Viewer.

Installation de l'application Record Viewer

Passons à un peu de code ! Pour découvrir l'API d’interface utilisateur, vous allez installer l’exemple d'application Record Viewer. Utilisez l'application Record Viewer pour créer, lire, mettre à jour et supprimer des enregistrements. Comme il s'agit d'une application d’exemple, elle comprend également une interface utilisateur qui affiche les réponses JSON à partir de l'API de l’interface utilisateur. Créez un enregistrement Opportunité dans l'application-exemple Record Viewer.
Conseil

Conseil

Pour réaliser le défi pratique à la fin de cette unité, vous devez suivre ces étapes. Si vous ne possédez pas de Trailhead Playground, descendez jusqu'au défi et créez-en un maintenant.

  1. Si vous n'avez pas installé Git, commencez par ça.
  2. Clonez le dépôt Record Viewer Git. Si vous ne savez pas comment cloner un référentiel, reportez-vous à la section Cloning a repository (en anglais) de l’aide GitHub.
  3. Record Viewer s'exécute dans un conteneur Docker. Installez la version stable de Docker Engine (Community). Docker vous demande de créer un compte pour télécharger le logiciel.
  4. Lancez Docker.
Remarque

Remarque

Sur un ordinateur Windows, dans les paramètres de Docker, choisissez Lecteurs partagés, puis sélectionnez le lecteur local sur lequel vous avez cloné l’application Record Viewer.

Record Viewer est une application connectée, c'est-à-dire qu'elle communique avec Salesforce via des API. L'application ne s'exécute pas sur la plateforme Salesforce, mais elle est connectée via des API. Pour définir comment l'application Record Viewer s'authentifie auprès de Salesforce, dans Configuration, créez une application connectée.

  1. Dans votre Trailhead Playground, à partir de Configuration, saisissez App et sélectionnez Gestionnaire des applications.
  2. Cliquez sur Nouvelle application connectée et saisissez ces paramètres :
    • Nom de l'application connectée : RecordViewer
    • Adresse e-mail du contact : Votre adresse e-mail
  3. Sélectionnez Activer les paramètres OAuth pour saisir ces paramètres :
    • URL de rappel : https://localhost:8443/oauth-redirect
    • Domaines OAuth sélectionnés : Accéder à vos données et les gérer (api)
  4. Cliquez sur Save.
  5. Sur la page Nouvelle application connectée, cliquez sur Continuer.
  6. Copiez et enregistrez la Clé consommateur (également appelée ID de client OAuth). Vous en aurez besoin par la suite.
Pour autoriser Record Viewer à effectuer des requêtes HTTP sur Salesforce à partir d'un navigateur Web, configurez un partage de ressources inter-origines (CORS) dans votre Trailhead Playground.
  1. Dans configuration, saisissez CORS, puis sélectionnez CORS.
  2. Cliquez sur Nouveau.
  3. Pour Modèle d'URL d'origine, saisissez https://localhost:8443
  4. Cliquez sur Save.

Pour créer l'application Record Viewer et lancer le serveur, utilisez une interface de ligne de commande, telle que le Terminal sous Mac OS ou l'invite de commande sous Windows.

Commencez par utiliser la commande cd pour passer au répertoire RecordViewer que vous avez cloné sur votre ordinateur local. Remplacez le chemin suivant par celui du répertoire RecordViewer. Par exemple, si le répertoire RecordViewer se trouve dans votre répertoire utilisateur Mac, saisissez cd ~/RecordViewer.
cd /replace/with/path/to/RecordViewer

Ensuite, exécutez les commandes Docker suivantes.

docker-compose build && docker-compose up -d
Pour lancer l'application Record Viewer, dans un navigateur, ouvrez https://localhost:8443.
Remarque

Remarque

L'application Record Viewer utilise un certificat SSL auto-signé, si bien que certains navigateurs peuvent vous informer que votre connexion n'est pas privée. Vous pouvez cliquer en toute sécurité et charger l'application. Dans Chrome, cliquez sur Avancé et Accéder au site. Dans Firefox, cliquez sur Avancé et ajoutez une exception. Dans Internet Explorer, cliquez sur Continuer sur ce site Web. Dans Safari, cliquez sur Afficher les détails et visiter ce site Web.

Remarque

Remarque

Si localhost refuse de se connecter, utilisez l'adresse IP de votre ordinateur, par exemple, https://192.168.34.34:8443. Utilisez également cette valeur dans le modèle d'URL d'origine CORS.

Pour autoriser Record Viewer à effectuer des requêtes vers Salesforce, saisissez l'URL de connexion de Salesforce, https://login.salesforce.com, et la clé consommateur que vous avez enregistrée à partir de l'application connectée, puis cliquez sur Connexion.L'écran de connexion de Record Viewer s'affiche.

Pour octroyer l'autorisation de l'application Record Viewer pour accéder à votre organisation Salesforce, vérifiez que votre nom d'utilisateur est correct, et cliquez sur Autoriser.Écran Autoriser l'accès.

Remarque

Remarque

Enfin, l'utilisateur est déconnecté de Salesforce. Pour réautoriser l'application, chargez la page de connexion : https://localhost:8443/login. Saisissez l'URL de connexion de Salesforce, https://login.salesforce.com, et la clé consommateur que vous avez enregistrée depuis l'application connectée.

Utilisation de l'application en Record Viewer

Utilisez l'application Record Viewer pour afficher, modifier, créer et supprimer des enregistrements Salesforce. Record Viewer utilise l'API de l’interface utilisateur pour effectuer ces opérations sur les enregistrements Salesforce. L'application vous offre également une manière pratique de rechercher la réponse JSON à partir de l'API de l’interface utilisateur.

Avant d'examiner un enregistrement dans Record Viewer, jetons un coup d'œil à un enregistrement dans Salesforce afin de pouvoir établir une comparaison.
  1. Dans un navigateur, ouvrez l'organisation Salesforce que vous avez utilisée pour créer l'application connectée RecordViewer.
  2. Cliquez sur Comptes et sur Nouveau.
  3. Pour le Nom du compte, saisissez Universal Containers
  4. Pour le numéro de téléphone, saisissez 206 555 1212 et cliquez sur Enregistrer.
  5. Pour afficher la page de détail du nouvel enregistrement, cliquez sur Détails.
Détails d'enregistrement de compte dans Lightning Experience
Examinons maintenant le même enregistrement dans l'application Record Viewer.
  1. Dans un navigateur, ouvrez https://localhost:8443.
  2. Dans Éléments récents, cliquez sur Universal Containers.
    Remarque

    Remarque

    Si vous ne voyez pas Universal Containers, actualisez la page.

L'application Record Viewer affiche la page des détails de l'enregistrement.Détail d'enregistrement de compte dans l'application Record Viewer

Nous avons parlé de la puissance de l'API d'IU, qui réside dans le fait que ces réponses respectent les modifications des métadonnées apportées au niveau de l'organisation Salesforce. Alors supprimons un champ de la présentation de Compte dans l'organisation Salesforce et voyons ce qui se passe dans Record Viewer.

Universal Containers possède un numéro de téléphone, mais la page des détails de l'enregistrement comprend également un champ Télécopie. Plus personne n'a de télécopieur, alors supprimons cela de la présentation de la page.

  1. Dans votre Trailhead Playground, à partir de Configuration, saisissez Object et sélectionnez Gestionnaire d'objet.
  2. Cliquez sur Compte | Présentations de page | Présentation de compte.
  3. Sur la page de l'éditeur de présentation, cliquez pour supprimer le champ Télécopie et cliquez sur Enregistrer.

Éditeur de présentation de page avec le champ Télécopie sélectionné.

Maintenant, revenez à l'application Record Viewer et cliquez sur Afficher l'enregistrement pour recharger l'enregistrement de Universal Containers. Voilà ! Le champ Télécopie a disparu et vous n'avez pas modifié une seule ligne de code.Détails de l'enregistrement de Compte dans l'application Record Viewer sans le champ de télécopie.

Dans ce cas, vous avez supprimé le champ de la présentation. Dans un scénario réel, administrateur n'a pas à vous indiquer quand il modifie une présentation : votre application se contente de répondre !

Structure de l'application Record Viewer

Pour travailler avec l'API de l’interface utilisateur, vous pouvez utiliser toutes les infrastructures Web ou les langages qui peuvent émettre des requêtes HTTP. Polymer, React, Angular, Vue, iOS, Android : utilisez la technologie de votre choix.

L'application Record Viewer est une application d'une page conçue à l'aide de React, Redux et Redux-Saga. Nous allons aborder rapidement les bases de ces technologies en examinant la structure des fichiers de l'application. Si vous ne connaissez pas bien React et Redux, ne vous inquiétez pas. Essayez simplement de saisir les idées générales.

Votre objectif est d'apprendre à accéder aux ressources de l'API de l’interface utilisateur et à gérer les réponses. Chaque infrastructure et chaque langage possède différentes manières de réaliser ces tâches, mais les concepts centraux sont identiques et l'API se comporte de la même façon.

L'application Record Viewer a été conçue à l'aide de :

  • Pug : génère des pages HTML à partir de modèles côté serveur.
  • React : une bibliothèque JavaScript pour développer des composants personnalisés et les combiner afin de réaliser des interfaces utilisateur.
  • Redux : maintient l'état d'une application React dans un store, qui est un objet JavaScript unique et immuable. Les composants React distribuent des objets JavaScript appelés actions, qui sont interceptés par des reducers. Les reducers sont des fonctions JavaScript qui utilisent les actions comme entrées pour mettre à jour l'état de Redux.
  • Redux-Saga : tout comme les reducers, les sagas sont des fonctions JavaScript qui s'exécutent lorsqu'elles interceptent une action. Les sagas gèrent les requêtes asynchrones vers l'API de l’interface utilisateur. À la fin d'une opération asynchrone, les sagas distribuent une action, qui peut être intercepté par un reducer, lequel met à jour le store. Les sagas évitent les enchevêtrements de rappels.
  • Node.js : runtime coté serveur pour les applications JavaScript

Voyons où se trouvent ces concepts abstraits dans le monde réel. Lancez votre IDE favoris et ouvrez le dossier RecordViewer que vous avez cloné à partir de GitHub. Structure de fichier de l'application Record Viewer en code VS. Un dossier actions, un dossier components, un dossier containers, un dossier helpers, un dossier reducers et un dossier sagas.

Commençons par examiner le dossier views qui contient les modèles HTML de Pug. L'application possède trois vues : la page de connexion, sur laquelle vous avez saisi les informations de connexion Salesforce, la page d'affichage des enregistrements, qui est le conteneur principal de l'application et une page d'erreur, que vous n'avez pas vue, du moins je l'espère. Le quatrième modèle Pug est une redirection d'authentification OAuth.

Si vous êtes authentifié avec succès, le modèle recordView.pug template se charge et appelle renderRoot, qui est défini dans le fichier root.js. À ce stade, l'application Record Viewer exécute du code côté client chargé dans cette page web unique.
<!-- /views/recordView.pug -->

doctype html
html
  block head
    title Record Viewer
    // Metadata and style sheet links removed to keep sample short

    script(type='text/javascript', src='https://code.jquery.com/jquery-2.0.3.min.js')
    script(type='text/javascript', src='root.js')

  body
    .slds-grid.slds-wrap
      nav.slds-col.slds-size--1-of-1
        .slds-page-header
          p.slds-page-header__title.slds-truncate.slds-align-middle
            a(href='/recordView') Record Viewer
          p.slds-page-header__info powered by User Interface API
    #root
    script.
      $( document ).ready(function() {
        let rootNode = document.getElementById("root");
        renderRoot("#{targetUrl}", "#{accessToken}", "#{recordId}", rootNode);
      });
Le fichier /client-src/root.js lance l'application. Il connecte le composant racine RecordViewerWrapper avec la racine des reducers d'état de Redux et le logiciel médiateur Saga. N'oubliez pas que les reducers mettent à jour l'état et que les sagas effectuent des requêtes vers l'API de l’interface utilisateur et distribuent les actions qui contiennent les résultats.
/* Excerpt from /client-src/root.js */

// Root renderer for record viewer.
global.renderRoot = function(instanceUrl, accessToken, recordId, rootNode) {
  const sagaMiddleware = createSagaMiddleware()
  const store = createStore(rootReducer, {login:{instanceUrl, accessToken}, record:{recordId}}, applyMiddleware(sagaMiddleware));
  sagaMiddleware.run(rootSaga);

  ReactDOM.render(
    <Provider store={store}>
      <div>
        <RecordViewerWrapper />
      </div>
    </Provider>,
    rootNode);
}
Le composant React RecordViewerWrapper.js se trouve dans le dossier containers. Ce composant est un composant de haut niveau connecté qui enveloppe le composant RecordViewer. La fonction mapStateToProps indique comment lier l'état de Redux aux propriétés de RecordViewer.
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */

// Presentational Component that uses state to decide how to
// construct the RecordViewer.
const mapStateToProps = (state) => {
  if (state.record.record) {
    return {
      screen: 'RECORD',
      record: state.record.record,
      headerRecordId: state.header.recordId,
      mode: state.record.mode,
      context: state.context,
      prevMode: state.record.prevMode,
      creds: state.login,
      picklists : state.picklists,
      depGraph: state.depGraph,
      rawjson: state.rawjson,
      error: state.error
    }
  } else if (state.record.recordId) {
    return {
      screen: 'FETCH_RECORD',
      recordId: state.record.recordId,
      mode: 'View',
      context: state.context,
      creds: state.login,
      rawjson: state.rawjson,
      error: state.error
    }
  }
 
  // Additional mappings removed to shorten sample
RecordViewWrapper possède également une fonction mapDispatchToProps qui mappe les actions de Redux dans les propriétés de React properties afin que les composants React puissent distribuer les actions en réponse à divers événements. Est-ce que vous vous souvenez des actions ? Elles sont utilisées comme entrées dans les reducers, lesquels sont des fonctions qui changent l'état de l'application.
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */

const mapDispatchToProps = (dispatch) => {
  return {
    onFetchRecord: (creds, id, context) => {
      dispatch(actions.fetchRecord(creds, id, context))
    },
    onRecordClick: (creds, id, context) => {
      dispatch(actions.fetchRecord(creds, id, context))
    },
    onViewRecordClick: (creds, id, context) => {
      dispatch(actions.fetchRecord(creds, id, context))
    },
    onFormFactorSelect: (formFactor, recordId) => {
      dispatch(actions.updateFormFactor(formFactor, recordId))
    },

   // Additional mappings removed to shorten sample
Enfin, la méthode connect() connecte ces deux cartes avec le composant simple RecordViewer.
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */

const RecordViewerWrapper = connect(
  mapStateToProps,
  mapDispatchToProps
) (RecordViewer)

export default RecordViewerWrapper

Les actions, reducers et sagas résident (assez confortablement) dans les dossiers actions, reducers et sagas.

Une action est un objet simple qui doit posséder une propriété type et qui peut avoir d'autres propriétés. La propriété type identifie l'action. Toutes les actions sont définies dans le fichier /actions/index.js.
/* Excerpt from /client-src/actions/index.js */

export const fetchEntities = (creds) => {
  return {
    type: 'FETCH_ENTITIES',
    creds
  }
}

export const receiveEntities = (entities) => {
  return {
    type: 'RECEIVE_ENTITIES',
    entities,
    receivedAt: Date.now()
  }
}
Suivons le chemin de l'action fetchEntities. Le fichier rootSaga.js enregistre toutes les sagas dans l'application Record Viewer. Lorsqu'une action est distribuée dans le store et que son type correspond à une chaîne dans la fonction takeEvery, la fonction lance la saga correspondante. Type d'action FETCH_ENTITIES engendre la saga entitiesFetcher.
/* /client-src/sagas/rootSaga.js */

import { takeEvery } from 'redux-saga'

import recentItemsFetcher from './recentItemsFetcher'
import recordFetcher from './recordFetcher'
import recordDeleter from './recordDeleter'
import recordUpdater from './recordUpdater'
import entitiesFetcher from './entitiesFetcher'
import recordCreator from './recordCreator'
import cloneDefaultsFetcher from './cloneDefaultsFetcher'
import createDefaultsFetcher from './createDefaultsFetcher'
import picklistsFetcher from './picklistsFetcher'
import depGraphValueUpdater from './depGraphValueUpdater'

export default function* rootSaga() {
  yield takeEvery('FETCH_RECORD', recordFetcher)
  yield takeEvery('FETCH_RECENT_ITEMS', recentItemsFetcher)
  yield takeEvery('DELETE_RECORD', recordDeleter)
  yield takeEvery('SAVE_RECORD', recordUpdater)
  yield takeEvery('FETCH_ENTITIES', entitiesFetcher)
  yield takeEvery('CREATE_RECORD', recordCreator)
  yield takeEvery('FETCH_CREATE_DEFAULTS', createDefaultsFetcher);
  yield takeEvery('FETCH_CLONE_DEFAULTS', cloneDefaultsFetcher);
  yield takeEvery('FETCH_PICKLISTS', picklistsFetcher);
  yield takeEvery('UPDATE_DEP_GRAPH_FIELD_VALUE', depGraphValueUpdater)
}
La saga entitiesFetcher.js effectue une requête vers l'API de l’interface utilisateur pour obtenir une liste des objets pris en charge par l'API. La ressource de l'API d'IU est /ui-api/object-info. Nous allons voir comment créer l'URL complète dans une unité ultérieure. Si vous êtes familier avec les API REST de Salesforce, vous avez déjà compris, car elles utilisent toutes le même modèle. En outre, tout comme les autres API REST Salesforce, l'API d’interface utilisateur utilise OAuth 2.0. Le jeton de porteur est transmis à l'en-tête Authorization. Nous avons configuré l'en-tête X-Chatter-Entity-Encoding sur false afin que la réponse ne soit pas encodée.
/* /client-src/sagas/entitiesFetcher.js */

import { call, put } from 'redux-saga/effects'

import { receiveEntities } from '../actions'

export default function* entitiesFetcher(action) {

  let mruUrl = action.creds.instanceUrl + '/services/data/v48.0/ui-api/object-info'

  let req = {
    method: 'GET',
    headers: {
      'Authorization' : 'Bearer ' + action.creds.accessToken,
      'X-Chatter-Entity-Encoding': false}
    }

  try {
    const response = yield call(fetch, mruUrl, req)
    const responseJson = yield response.json()
    yield put(receiveEntities(responseJson))
  } catch(err) {
    console.error('Describe sobjects error: ' + JSON.stringify(err))
  }
}
Voyez-vous yield put(receiveEntities(responseJson)) ? Recherchez receiveEntities dans l'exemple de code précédent à partir de /actions/index.js. Voyez-vous l'action receiveEntities ? Oui, si l'opération de l'API d'IU a réussi, la saga envoie l'action receiveEntities contenant la réponse JSON. Le reducer entities.js intercepte l'action et l'utilise comme une entrée pour mettre à jour l'état de Redux.
/* /client-src/reducers/entities.js */

const entities = (state = {sobjects: []}, action) => {
  switch (action.type) {
    case 'RECEIVE_ENTITIES':
      return {
        sobjects: action.entities.objects,
        receivedAt: action.receivedAt
      }
    default:
      return state
  }
}

export default entities

Le reducer met à jour une partie de l'état de Redux. Une fois le changement effectué, les composants React qui adhèrent à ce bit d'état sont automatiquement mis à jour.

Dans l'application Record Viewer, les composants React qui construisent HTML résident dans le dossier components. Ces composants définissent des morceaux de l'interface utilisateur de l'application. Le composant CreateableEntitiesList.js définit l'IU qui affiche un menu dans l'application Record Viewer. Le menu contient la liste des objets pris en charge renvoyés à partir de l'appel à /ui-api/object-info. L'application RecordViewer reçoit les entités et les affiche dans le composant CreateableEntitiesList.Menu contextuel Créer un enregistrement avec une liste des objets à créer.
Conseil

Conseil

Le code RecordViewer utilise souvent le terme entities (entités), qui signifie objects (objets). De sorte que lorsque vous voyez quelque chose comme CreateableEntitiesList, vous pouvez le traduire par « liste d'objets qui peuvent être créés ». En d'autres termes, vous pouvez créer un enregistrement à partir de tous les objets de cette liste ! Ces objets sont pris en charge par l'API de l’interface utilisateur.

Dans React, vous composez des composants pour créer une IU, ce qui signifie que les composants parents peuvent être composés de composants enfants imbriqués. Lorsque vous développez une application, divisez la fonctionnalité en composants simples et combinez-les pour former des composants plus complexes.

Lorsque vous regardez les noms des composants dans le dossier components, vous pouvez affirmer que l'application Record Viewer est centrée sur un travail avec des enregistrements : Record.js, RecordButton.js, RecordSection.js. Regardez le code du composant et vous pourrez remarquer les composants enfants imbriqués. Par exemple, le composant RecordSection comprend les composants imbriqués RecordRow.
/* Excerpt from /client-src/components/RecordSection.js */

// Component that displays a Record section.
const RecordSection = ({section, error, editValues, picklists, onFieldValueUpdate, allowEdit, index, objectInfo, onEditDepGraph, uiMode, recordView}) => {

  return (
    <tbody>
      { section.useHeading &&
       <tr>
         <td colSpan="4" key={'sectionHeading' + index} className="slds-text-heading--small slds-p-left--medium slds-p-top--small slds-p-bottom--medium">
           {section.heading}
         </td>
       </tr>
      }
      {section.rows.map((row, i) =>
        <RecordRow
          key={'sectionRow' + index + ',' + i}
          allowEdit={allowEdit}
          uiMode={uiMode}
          picklists={picklists}
          onFieldValueUpdate={onFieldValueUpdate}
          error={error}
          editValues={editValues}
          row={row}
          sectionIndex={index}
          rowIndex={i} 
          objectInfo={objectInfo}
          recordView={recordView}
          onEditDepGraph={onEditDepGraph}/>
      )}
   </tbody>
  );
}
Par vous-même, accédez à votre IDE et examinez le composant Record (Record.js). Il comprend un composant <RecordSection>, un composant <DepGraphEditor> et plusieurs composants <RecordButton>.

Enfin, et surtout, le dossier helpers contient certaines fonctions d'aide JavaScript. Ces fonctions permettent de transformer les réponses provenant de l'API de l’interface utilisateur en des modèles de données qui alimentent l'IU de Record Viewer. Le code recordLayout.js développe un modèle de données interne pour les présentation, et le code depGraphHelper.js développe l'éditeur du sélecteur de liste dépendant. Nous examinerons ces fichiers ultérieurement.