Suivez votre progression
Accueil Trailhead
Accueil Trailhead

Élaboration d'une interface utilisateur pour les listes de sélection dépendantes

Objectifs de formation

Une fois cette unité terminée, vous pourrez :
  • Faire une requête à l'API de l’interface utilisateur pour obtenir toutes les valeurs de la liste de sélection pour un type d'enregistrement.
  • Nommer la propriété qui contient une carte des champs dépendants d'un objet.
  • Nommer la propriété qui contient une liste des champs de contrôle d'un champ.

En quoi les listes de sélection dépendantes sont-elles particulières ?

Il n'est pas évident de créer une interface utilisateur pour des champs de listes de sélection dépendantes, mais l'API de l’interface utilisateur simplifie les choses. Les valeurs d'un champ de listes de sélection dépendantes sont filtrées en fonction d'une sélection dans une autre liste de sélection ou d'une case à cocher (appelée champ de contrôle). Examinez cette capture d'écran de l'éditeur de listes de sélection dépendantes dans l'application Record Viewer. Une liste de sélection de pays contrôle une liste de sélection d'États/régions (qui dépend de la liste de sélection Pays). Lorsqu'un utilisateur sélectionne un pays, les valeurs de État/Province sont filtrées en conséquence. Lorsqu'un utilisateur sélectionne un état ou une province, cette sélection filtre les valeurs de Ville. L'éditeur de listes de sélection dépendantes affiche États-Unis, Californie, Los Angeles.

Vous avez compris le principe : les listes de sélection dépendantes permettent de renseigner un formulaire plus rapidement, car les valeurs sont filtrées. Cela vous permet de passer moins de temps à les faire défiler. Et un éditeur de listes de sélection dépendantes facilite la modification des champs connexes, en particulier sur un appareil mobile. Imaginez comme il pourrait être difficile de passer d'un champ à l'autre lorsque les valeurs dépendantes changent. Il est beaucoup plus simple de modifier toutes les valeurs de la liste de sélection correspondante dans une boîte de dialogue.

Mais ce sont ces relations entre les champs de contrôle et les champs dépendants qui rendent difficile la création d'une interface utilisateur. Une liste de sélections (ou une case à cocher) peut contrôler plusieurs listes de sélection dépendantes. Et une liste de sélection dépendante est contrôlée, mais elle peut également exercer elle-même un contrôle. En réalité, une liste de sélection dépendante peut contrôler plusieurs autres listes de sélection dépendantes. La hiérarchie des champs de listes de sélection dépendantes est un arbre. Et un objet peut avoir une forêt d'arbres.

L'API de l’interface utilisateur existe pour résoudre les problèmes pour les développeurs d'IU, de sorte que nous avons dévoilé les propriétés qui décrivent ces relations de champ.
  • objectInfo.dependentFields contient une carte des champs dépendants d'un objet.
  • objectInfo.fields[fieldName].controllingFields contient une liste des champs de contrôle d'un champ.
Nous avons également montré une ressource qui récupère toutes les valeurs d'une liste de sélection pour un type d'enregistrement spécifié.
GET /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}

L'application en Record Viewer utilise ces propriétés et ressources pour créer son propre éditeur de listes de sélection dépendantes.

Utilisation de l'éditeur de listes de sélection dépendantes

Pour afficher l'éditeur de listes de sélection dépendantes dans l'application Record Viewer, créez une relation dépendante entre au moins deux champs sur un objet.

Dans votre Trailhead Playground, créez une dépendance de champ pour l'objet Compte. Puis, nous allons éditer un compte dans l'application Record Viewer pour voir l'éditeur de listes de sélection dépendantes en action.

  1. Dans Configuration, saisissez object et sélectionnez Gestionnaire d'objet.
  2. Sélectionnez Compte | Champs et relations | Dépendances de champs.
  3. Cliquez sur Nouveau.
  4. Créerons une relation entre deux champs existants. Pour le champ de contrôle, sélectionnez Notation. Pour le champ dépendant, sélectionnez Priorité clients. Cliquez sur Continuer.
  5. Modifiez les dépendances du champ afin que Hot comprenne High, Warm comprenne Low et Medium et que Cold comprenne Low. Cliquez sur Enregistrer.Modifiez la matrice de dépendance de champ dans Salesforce.
  6. Ouvrez l'application Record Viewer et sélectionnez un compte dans la liste des événements récents.
  7. Cliquez sur Modifier.
  8. À côté des champs Notation du compte et Priorité clients, cliquez sur l'icône du crayon. Icône du crayon en dessous du champ Notation du compte

    La boîte de dialogue de l'éditeur de listes de sélection dépendantes s'ouvre. Utilisez-la pour sélectionner une valeur pour les deux champs. Lorsque vous sélectionnez une valeur pour un champ, les valeurs de l'autre champ sont filtrées en fonction de vos choix dans Configuration.Éditeur de listes de sélection dépendantes avec les champs Notation du compte et priorité clients

Comment les listes de sélection dépendantes sont-elles exposées dans l'API de l’interface utilisateur ?

Les champs des listes de sélection dépendantes font partie d'une arborescence de dépendance qui est une hiérarchie de contrôle et de champs dépendants. Chaque nœud est un arbre qui peut posséder un certain nombre de nœuds enfant. Un nœud sans enfant est appelé une feuille.

Notre arborescence de dépendance est simple car elle ne possède qu'un champ racine Rating, lequel contrôle un champ CustomerPriority__c. Si Rating avait contrôlé un autre champ, notre hiérarchie de dépendance commencerait à ressembler à un arbre. Mais avec seulement deux champs, notre arbre de dépendance ne possède aucune branche, seulement un tronc. Rating contrôle CustomerPriority.

Un objet peut avoir n'importe quel nombre de ces arbres de dépendance. Pour créer une IU qui permet à des utilisateurs de modifier des listes de sélection dépendantes, vous devez connaître la hiérarchie complète de chaque arbre.

Par exemple, l'objet Compte pourrait posséder deux arbres de dépendance, l'un dont la racine est Pays et un autre dont la racine est Rating. Ces arbres de dépendance tirés de nos exemples sont simples, car chaque champ de contrôle ne contrôle qu'un seul champ dépendant. Cependant, vous pouvez imaginer que le champ Pays, en plus de contrôler le champ État/Province, contrôle également un champ Langue et un champ Fuseau horaire.

Comme vous l'avez appris, l'API de l’interface utilisateur possède de propriété de réponse qui offre la structure de dépendance de champs. Les propriétés sont toutes deux incluses dans la charge de travail objectInfo, qui fait partie de la réponse de /ui-api/record-ui/{recordIds}. Pour voir la réponse JSON, ouvrez un objet de compte dans l'application Record Viewer et cliquez sur Show JSON. JSON dans l'application RecordView.

objectInfo.dependentFields
Cette propriété offre des arbres de champ dépendant pour tous les champs sur un objet. Cet exemple JSON possède un champ racine, Rating, mais un objet pourrait avoir plusieurs champs racine.
"objectInfos" : {
    "Account" : {
      ...
      "dependentFields" : {
        "Rating" : {
          "CustomerPriority__c" : { }
        }
      },
      ...
Lorsqu'un objet possède une hiérarchie étendue de champs dépendants, tel que notre exemple Pays, État/Province, Ville, la propriété dependentFields les imbriquent jusqu'à ce qu'ils atteignent un nœud feuille, tel que Ville.
"objectInfos" : {
    "Account" : {
      ...
      "dependentFields": {
        "Country__c": {
          "StateProvince__c": {
            "City__c": {}
          }
        }
      },
      ...
objectInfo.fields[fieldName].controllingFields
Cette propriété fournit la liste des champs qui contrôlent ce champ particulier, à commencer par le champ qui contrôle immédiatement ce champ. Dans notre exemple simple, le champ CustomerPriority__c possède un champ de contrôle, Rating.
"objectInfos" : {
    "Account" : {
      ...
      "fields" : {
       ...
        "CustomerPriority__c" : {
            "apiName" : "CustomerPriority__c",
            ...
            "controllingFields" : [ "Rating" ],
            ...
Lorsqu'un objet possède une hiérarchie étendue de champs de contrôle, tel que notre exemple Pays, État/Province, Ville, la propriété controllingFields en dresse la liste dans l'ordre en commençant par le champ de contrôle immédiat.
"objectInfos" : {
    "Account" : {
      ...
      "fields" : {
       ...
        "City__c" : {
            "apiName" : "City__c",
            ...
            "controllingFields" : [ "StateProvince__c", "Country__c"],
            ...
En plus de connaître la relation entre les champs dépendants, nous devrons également connaître les valeurs de chaque champ de la liste de sélection sur un objet en relation avec son champ de contrôle immédiat, car il s'agit du champ qui filtre les valeurs. L'API de l’interface utilisateur nous fournit un point de terminaison pratique pour obtenir toutes ces valeurs pour un type d'enregistrement d'un simple appel.
GET /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}
Cet extrait de la réponse JSON montre les valeurs du champ CustomerPriority__c.
"CustomerPriority__c" : {
      "controllerValues" : {
        "Hot" : 0,
        "Warm" : 1,
        "Cold" : 2
      },
      "defaultValue" : null,
      "url" : "/services/data/v48.0/ui-api/object-info/Account/picklist-values/012000000000000AAA/CustomerPriority__c",
      "values" : [ {
        "attributes" : null,
        "label" : "High",
        "validFor" : [ 0 ],
        "value" : "High"
      }, {
        "attributes" : null,
        "label" : "Low",
        "validFor" : [ 1, 2 ],
        "value" : "Low"
      }, {
        "attributes" : null,
        "label" : "Medium",
        "validFor" : [ 1 ],
        "value" : "Medium"
      } ]
    },

La propriété controllerValues fournit les valeurs et les index de la liste de sélection du champ de contrôle immédiat. De sorte que dans notre exemple, le champ est Customer Priority, et le champ de contrôle immédiat est Rating. Les valeurs de Rating sont Hot, Warm et Cold, et les index sont 0, 1 et 2.

Pour chaque valeur de Customer Priority (High, Low et Medium), la propriété validFor contient les valeurs de Rating qui, lorsqu'elles sont sélectionnées dans une liste de sélection, comprennent la valeur Customer Priority, au lieu de la filtrer.

Par exemple lorsque Rating vaut Hot (0), la liste de sélection de Customer Priority est filtrée pour inclure uniquement High. Lorsque Rating vaut Warm (1), la liste de sélection de Customer Priority est filtrée pour inclure Low etMedium. Lorsque Rating vaut Cold (2), la liste de sélection de Customer Priority est filtrée pour n'inclure que Low.

Maintenant que nous savons de quelles informations nous avons besoin et comment les obtenir à partir de l'API de l’interface utilisateur, voyons comment l'application Record Viewer construit son éditeur de listes de sélection dépendantes.

Récupération des valeurs de la liste de sélection

Lorsqu'un utilisateur clique sur Créer, Cloner ou Modifier un enregistrement, l'application Record Viewer analyse les valeurs de la liste de sélection de tous les champs sur l'objet pour le type d'enregistrement spécifié.

Vous pouvez voir le code dans RecordViewerWrapper.js. Recherchez un appel à dispatch(actions.fetchPicklists(creds, apiName, recordType)) pour chaque scénario : onNewRecordClick, onCloneClick et onEditClick.

Pour voir le code qui construit l'URL de l'API d'IU qui analyse les valeurs de la liste de sélection, examinez la saga picklistFetcher.js.
/* /sagas/picklistFetcher.js */

let url = action.creds.instanceUrl + '/services/data/v48.0/ui-api/object-info/' 
          + action.apiName + '/picklist-values/' + action.recordType + '/';
Le reducer picklists.js met à jour le store Redux avec les valeurs de la liste de sélection.
/* /reducers/picklists.js */

const picklists = (state = {values: undefined}, action) => {
  switch (action.type) {
    case 'RECEIVE_PICKLISTS':
      return {
        fieldValues : action.result.picklistFieldValues
      }
    case 'FETCH_PICKLISTS': // Clear values when new collection is requested.
      return {
        fieldValues: undefined
      }
    default:
      return state
  }
}

export default picklists

Affichage de l'éditeur de listes de sélection dépendantes

Lorsqu'un enregistrement est en mode de modification, l'application affiche l'icône d'un crayon en dessous des champs dépendants pour indiquer que l'utilisateur peut ouvrir l'éditeur de listes de sélection dépendantes. Icône du crayon en dessous des champs de la liste de sélection dépendante.

Lorsque l'utilisateur clique sur l'icône du crayon, l'application ouvre l'éditeur de listes de sélection dépendantes dans une boîte de dialogue modale. En code, l'éditeur de listes de sélection dépendantes est appelé composant DepGraphEditor.Éditeur de la listes de sélection dépendantes

Le code qui effectue ce travail est le composant RecordRow.js. Il vérifie si le champ existe dans une arborescence de dépendance ou non et identifie le champ racine de l'arborescence de dépendance. Il affiche l'icône du crayon en dessous des champs et ouvre la boîte de dialogue modale lorsque l'utilisateur clique sur l'icône du crayon.
/* /components/RecordRow.js */

  if (objectInfo 
    && ((component.field && component.field in objectInfo.dependentFields)
       || (component.fieldInfo && component.fieldInfo.controllingFields.length > 0))) {
    // Last field in controlling fields is the root field
    let lastControllingIndex =  component.fieldInfo.controllingFields.length - 1
    let rootField = null
    if (component.field in objectInfo.dependentFields){
      rootField = component.field
    } else {
      rootField = component.fieldInfo.controllingFields[lastControllingIndex]
    }
    
    // Retrieve the picklist fields that need to show up.
    const subfieldTree = objectInfo.dependentFields[rootField]
    const modalFields = getFlattenedTree(objectInfo, subfieldTree, rootField)

    // Open a modal on click of the input field.
    let fieldTree = {}
    fieldTree[rootField] = subfieldTree

    return (
      <div>
        <label
          key={'componentInput' + itemLabel + ',' + i}
          onClick={(event) => onEditDepGraph(picklists, modalFields, editValues, fieldTree, uiMode.toString())}
          value={currPicklistValue}>
          {editValues[component.field].current}
        </label>
        <button className="fa fa-pencil"
        key={'componentInput' + itemLabel + 'button,' + i}
        onClick={(event) => onEditDepGraph(picklists, modalFields, editValues, fieldTree, uiMode.toString())}>
        </button>
      </div>
    );
  }

Pour ouvrir la boîte de dialogue modale, la méthode onClick() invoque la méthode onEditDepGraph(), qui distribue l'action EDIT_DEP_GRAPH, laquelle change le mode en EditDepGraph. La boîte de dialogue modale s'ouvre lorsque le mode change.

Pour voir le changement de mode, observez le code du reducer record.js.
/* /reducers/record.js */

case 'EDIT_DEP_GRAPH':
  return {
    ...state,
    prevMode: action.prevMode, // save previous mode to return to on close
    mode: 'EditDepGraph'
  }
Pour voir comment la boîte de dialogue modale est ouverte en changement de mode, regardez le code du composant record, qui renvoie le composant DepGraphEditor pour le mode EditDepGraph.
/* /components/Record.js */

<div>
    {uiMode === 'EditDepGraph' &&
      <DepGraphEditor 
          depGraph={depGraph}
          picklists={picklists}
          editValues={recordView.editValues}
          onFieldValueUpdate={onDepGraphFieldValueUpdate}
          onClose={() => onDepGraphClose(prevMode)}/> }

Mise à jour des valeurs de la liste de sélection

Lorsqu'un utilisateur sélectionne une nouvelle valeur pour un champ de listes de sélection, l'application doit mettre à jour les valeurs de ses champs de listes de sélection enfant. Et si ces enfants ont des enfants, l'application doit également les mettre à jour, jusqu'aux champs feuilles de la liste de sélection, qui n'ont pas d'enfants. Pour effectuer ces mises à jour, il est important de connaître la sous-arborescence de dépendance sous-jacente au champ dont la valeur a changé.

En outre, si la valeur présélectionnée d'un champ reste valide, l'application doit préserver cette valeur.

Pour consulter le code extrayons la saga depGraphValueUpdater.js. Le code obtient la structure arborescente de dépendances sous-jacentes au champ avec une nouvelle valeur en utilisant la propriété controllingFields et la propriété fieldTree que nous avons extraite dans le composant RecordRow.
/* /sagas/depGraphValueUpdater.js */

// Traverse down to the target field in the tree.

const field = modalFieldsMap[action.field];
var treeNode = action.fieldTree;
for (var i = field.controllingFields.length -1; i>=0; i--) {
  var controllingField = field.controllingFields[i]
  treeNode = treeNode[controllingField];
}

treeNode = treeNode[action.field];

// Now treeNode is the subtree rooted at the target field.

Grâce à cette structure de sous-arborescence extraite et aux valeurs de la liste de sélection obtenues lorsque l'utilisateur a cliqué sur Créer, Cloner ou Modifier un enregistrement, nous pouvons créer des valeurs légales de listes de sélection pour chaque champ de listes de sélection de la sous-arborescence.

Pour voir comment l'application obtient les valeurs légales de la liste de sélection d'un champ particulier, consultez getLegalValues() dans /helpers/depGraphHelper.js. Pour voir comment le code traverse la sous-arborescence et renseigne récursivement les valeurs légales de chaque champ enfant de la liste de sélection à l'aide de la première recherche étendue, examinez la saga depGraphValueUpdater.js.

Félicitations, il n'y a rien d'autre à ajouter ! Certes, on ne peut pas dire que la création d'une IU pour des listes de sélection dépendantes soit du gâteau, mais elle reste tout de même plus facile (et tout aussi savoureuse) qu’un soufflé au fromage

Pour résumer : pour créer une IU pour un éditeur de listes de sélection dépendantes, l'API de l’interface utilisateur vous fournit toutes les informations dont vous avez besoin dans trois emplacements, la propriété objectInfo.dependentFields, la propriété objectInfo.fields[fieldName].controllingFields et la réponse du point de terminaison /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}.

L'API de l’interface utilisateur vous permet de créer de manière flexible des interfaces utilisateur Salesforce personnalisées qui réjouiront vos clients. Nous espérons que l'application Record Viewer vous permettra de prendre un bon départ. N'hésitez pas à copier le dépôt GitHub et à renvoyer des pull requests. Nous sommes impatients d'entendre parler de vos applications !