Empiece a realizar un seguimiento de su progreso
Inicio de Trailhead
Inicio de Trailhead

Construir una interfaz de usuario para listas de selección dependientes

Objetivos de aprendizaje

Después de completar esta unidad, podrá:
  • Realizar una solicitud a la API de la interfaz de usuario para obtener todos los valores de lista de selección para un tipo de registro.
  • Nombrar la propiedad contiene un mapa de los campos dependientes de un objeto.
  • Nombrar la propiedad contiene una lista de los campos de control de un campo.

¿Qué tienen de especial las listas de selección dependientes?

Es complicado construir una interfaz de usuario para campos de listas de selección dependientes, pero la API de la interfaz de usuario lo facilita. Los valores de un campo de lista de selección dependiente se filtran en base a una selección en otra lista de selección o casilla de verificación (denominado campo de control). Mire esta captura de pantalla del editor de listas de selección dependientes en la aplicación Record Viewer. Una lista de selección País controla una lista de selección Estado/Provincia (que depende de la lista de selección País). Cuando un usuario selecciona un país, los valores de Estado/Provincia se filtran para coincidir. Cuando un usuario selecciona un estado o una provincia, esa selección filtra los valores de Ciudad. Editor de listas de selección dependientes mostrando EE.UU., California, Los Ángeles.

Ya se lo imaginará, las listas de selección dependientes aceleran el relleno de un formulario porque los valores se filtran, de modo que tiene que emplear menos tiempo en desplazarse. Además, un editor de listas de selección dependientes facilita modificar campos relacionados, especialmente en un dispositivo móvil. Imagínese lo difícil que sería desplazarse de campo a campo a medida que cambiaran los valores dependientes. Es mucho más fácil cambiar todos los valores de la lista de selección relacionada en un cuadro de diálogo.

Pero son estas relaciones entre los campos de control y los campos dependientes las que dificultan la creación de una interfaz de usuario. Una lista de selección (o una casilla de verificación) puede controlar múltiples listas de selección dependientes. Y una lista de selección dependiente está controlada, pero también puede controlar. De hecho, una lista de selección dependiente puede controlar varias listas de selección dependientes adicionales. La jerarquía de campos de las listas de selección dependientes es un árbol. Y un objeto puede tener un bosque de árboles.

La API de la interfaz de usuario existe para resolver problemas para los desarrolladores de interfaces de usuario, de modo que exponemos las propiedades que describen estas relaciones de campos.
  • objectInfo.dependentFields contiene un mapa de los campos dependientes de un objeto.
  • objectInfo.fields[nombreCampo].controllingFields contiene una lista de los campos de control de un campo.
También exponemos un recurso que obtiene todos los valores de lista de selección para un tipo de registro especificado.
GET /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}

La aplicación Record Viewer utiliza estas propiedades y el recurso para construir su editor de listas de selección dependientes.

Utilizar el editor de listas de selección dependientes

Para ver el editor de listas de selección dependientes en la aplicación Record Viewer, cree una relación dependiente entre al menos dos campos en un objeto.

En su Trailhead Playground, cree una dependencia de campo para el objeto Cuenta. Luego modificaremos una cuenta en la aplicación Record Viewer para ver el editor de listas de selección dependientes en acción.

  1. Desde Configuración, ingrese objeto y seleccione Gestor de objetos.
  2. Seleccione Cuenta | Campos y relaciones | Dependencias de campos.
  3. Haga clic en Nuevo.
  4. Creemos una relación entre dos campos existentes. Para el campo de control, seleccione Rating. Para el campo dependiente, seleccione Customer Priority. Haga clic en Continuar.
  5. Modifique las dependencias de campos de modo que Hot incluya High, Warm incluya Low y Medium, y Cold incluya Low. Haga clic en Guardar.Modificar la matriz de dependencias de campos en Salesforce.
  6. Abra la aplicación Record Viewer y seleccione una cuenta en la Lista de elementos recientes.
  7. Haga clic en Modificar.
  8. Junto a los campos Valoración de la cuenta o Prioridad de cliente, haga clic en el icono de lápiz. Icono de lápiz junto al campo Valoración de la cuenta.

    Se abre el cuadro de diálogo del editor de listas de selección dependientes. Utilícelo para seleccionar un valor para ambos campos. Cuando seleccione un valor para un campo, los valores del otro campo se filtran en base a sus selecciones en Configuración.Editor de listas de selección dependientes con los campos Valoración de la cuenta y Prioridad de cliente.

Cómo se exponen las listas de selección dependientes en la API de la interfaz de usuario

Las listas de selección dependientes forman parte de un árbol de dependencias, que es una jerarquía de control y campos dependientes. Cada nodo de un árbol puede tener cualquier número de nodos secundarios. Un nodo sin secundarios se denomina hoja.

Nuestro árbol de dependencias de ejemplo es sencillo porque tiene un campo raíz, Rating, que controla un campo CustomerPriority__c. Si Rating controlara otro campo, nuestra jerarquía de dependencias empezaría a tener un aspecto de árbol. Pero con solo dos campos, nuestro árbol de dependencias no tiene ramas, es solo un tronco. Rating controla CustomerPriority.

Un objeto puede tener cualquier número de estos árboles de dependencias. Para construir una interfaz de usuario que permita a los usuarios modificar listas de selección dependientes, tiene que conocer la jerarquía completa de cada árbol.

Por ejemplo, el objeto Cuenta podría tener dos árboles de dependencias, una cuya raíz fuera País y una cuya raíz fuera Rating. Estos árboles de dependencias de nuestros ejemplos son sencillos, porque cada campo de control controla únicamente un campo dependiente. Puede imaginar, sin embargo, que el campo País, además de controlar el campo Estado/Provincia, también podría controlar un campo Idioma y un campo Zona horaria.

Como aprendió, la API de la interfaz de usuario tiene dos propiedades de respuesta que proporcionan la estructura de dependencia de campos. Ambas propiedades se incluyen en la carga de trabajo objectInfo, que forma parte de la respuesta desde /ui-api/record-ui/{recordIds}. Para ver la respuesta JSON, abra un objeto de cuenta en la aplicación Record Viewer, y haga clic en Show JSON (Mostrar JSON). JSON en la aplicación Record Viewer.

objectInfo.dependentFields
Esta propiedad proporciona los árboles de campos dependientes para todos los campos de un objeto. Este JSON de ejemplo tiene un campo raíz, Rating, pero un objeto podría tener múltiples campos raíz.
"objectInfos" : {
    "Account" : {
      ...
      "dependentFields" : {
        "Rating" : {
          "CustomerPriority__c" : { }
        }
      },
      ...
Cuando un objeto tiene una jerarquía ampliada de campos dependientes, como en nuestro ejemplo de País, Estado/Provincia, Ciudad, la propiedad dependentFields los anida hasta que alcanza un nodo hoja, como Ciudad.
"objectInfos" : {
    "Account" : {
      ...
      "dependentFields": {
        "Country__c": {
          "StateProvince__c": {
            "City__c": {}
          }
        }
      },
      ...
objectInfo.fields[nombreCampo].controllingFields
Esta propiedad proporciona la lista de campos que controlan este campo en particular, empezando desde el campo que controla inmediatamente este campo. En nuestro sencillo ejemplo, el campo CustomerPriority__c tiene un campo de control, Rating.
"objectInfos" : {
    "Account" : {
      ...
      "fields" : {
       ...
        "CustomerPriority__c" : {
            "apiName" : "CustomerPriority__c",
            ...
            "controllingFields" : [ "Rating" ],
            ...
Cuando un objeto tiene una jerarquía ampliada de campos de control, como en nuestro ejemplo de País, Estado/Provincia, Ciudad, la propiedad controllingFields los enumera en orden empezando por el campo de control inmediato.
"objectInfos" : {
    "Account" : {
      ...
      "fields" : {
       ...
        "City__c" : {
            "apiName" : "City__c",
            ...
            "controllingFields" : [ "StateProvince__c", "Country__c"],
            ...
Además de saber la relación entre los campos dependientes, también necesitamos conocer los valores de cada campo de lista de selección en un objeto en relación con su campo de control inmediato, debido a que ese es el campo que filtra los valores. La API de la interfaz de usuario nos da un útil extremo para obtener todos esos valores para un tipo de registro en una única llamada.
GET /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}
Este resumen de la respuesta JSON muestra los valores del campo 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 propiedad controllerValues proporciona los valores y los índices de lista de selección del campo de control inmediato. Así que en nuestro ejemplo, el campo es Customer Priority, y el campo de control inmediato es Rating. Los valores de Rating son Hot, Warm y Cold, y los índices son 0, 1 y 2.

Para cada valor de Customer Priority (High, Low y Medium), la propiedad validFor contiene los valores de Rating que, cuando se seleccionan en una lista de selección, incluyen el valor de Customer Priority, en vez de filtrarlo.

Por ejemplo, cuando Rating es Hot (0), la lista de selección Customer Priority se filtra para incluir únicamente High. Cuando Rating es Warm (1), la lista de selección Customer Priority se filtra para incluir Low y Medium. Cuando Rating es Cold (2), la lista de selección Customer Priority se filtra para incluir únicamente Low.

Ahora que comprendemos la información que necesitamos y cómo obtenerla de la API de la interfaz de usuario, miremos cómo la aplicación Record Viewer construye su editor de listas de selección dependientes.

Obtener los valores de la lista de selección

Cuando un usuario hace clic en Crear, Duplicar o Modificar un registro, la aplicación Record Viewer captura previamente los valores de la lista de selección para todos los campos del objeto para el tipo de registro especificado.

Puede ver el código en RecordViewerWrapper.js. Busque una llamada a dispatch(actions.fetchPicklists(creds, apiName, recordType)) para cada escenario: onNewRecordClick, onCloneClick y onEditClick.

Para ver el código que construye la URL de la API de la interfaz de usuario que captura los valores de la lista de selección, mire la saga de picklistFetcher.js.
/* /sagas/picklistFetcher.js */

let url = action.creds.instanceUrl + '/services/data/v48.0/ui-api/object-info/' 
          + action.apiName + '/picklist-values/' + action.recordType + '/';
El reductor de picklists.js actualiza el almacén Redux con los valores de la lista de selección.
/* /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

Visualizar el editor de listas de selección dependientes

Cuando un registro está en modo de modificación, la aplicación muestra un icono de lápiz junto a los campos dependientes para indicar que el usuario puede abrir el editor de listas de selección dependientes. Icono de lápiz junto a campos de lista de selección dependiente.

Cuando el usuario hace clic en el icono de lápiz, la aplicación abre el editor de listas de selección dependientes como un cuadro de diálogo modal. En código, el editor de listas de selección dependientes se denomina el componente DepGraphEditor.Editor de listas de selección dependientes

El código que hace este trabajo es el componente RecordRow.js. Comprueba si el campo existe o no en un árbol de dependencia e identifica el campo raíz del árbol de dependencia. Representa el icono de lápiz junto a los campos y abre el cuadro de diálogo modal cuando el usuario hace clic en el icono de lápiz.
/* /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>
    );
  }

Para abrir el cuadro de diálogo modal, el método onClick() invoca el método onEditDepGraph(), que despacha la acción EDIT_DEP_GRAPH, que cambia el modo a EditDepGraph. El cuadro de diálogo modal se abre cuando cambia el modo.

Para ver el cambio de modo, mire el código reductor de record.js.
/* /reducers/record.js */

case 'EDIT_DEP_GRAPH':
  return {
    ...state,
    prevMode: action.prevMode, // save previous mode to return to on close
    mode: 'EditDepGraph'
  }
Para ver cómo se abre el cuadro de diálogo modal en el cambio de modo, mire el código del componente Registro, que devuelve el componente DepGraphEditor para el modo EditDepGraph.
/* /components/Record.js */

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

Actualizar los valores de la lista de selección

Cuando un usuario selecciona un nuevo valor para un campo de lista de selección, la aplicación debe actualizar los valores de sus campos de lista de selección secundarios. Y si esos secundarios tienen secundarios a su vez, la aplicación debe actualizarlos también, hasta llegar a los campos de lista de selección hoja, que no tienen secundarios. Para realizar estas actualizaciones, es importante conocer el subárbol de dependencias por debajo del campo cuyo valor cambió.

Del mismo modo, si el valor preseleccionado para un campo se mantiene válido, la aplicación debe preservar ese valor.

Para ver el código, consulte la saga de depGraphValueUpdater.js. El código obtiene la estructura del árbol de dependencias por debajo del campo con un nuevo valor utilizando la propiedad controllingFields y la propiedad fieldTree que extrajimos en el componente 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.

El uso de esta estructura del subárbol extraído y los valores de lista de selección obtenidos cuando el usuario hizo clic en Crear, Duplicar o Modificar un registro, construimos los valores de lista de selección legales para cada campo de lista de selección del subárbol.

Par ver cómo la aplicación obtiene los valores de lista de selección legales para un campo en particular, consulte getLegalValues() en /helpers/depGraphHelper.js. Para ver cómo el código recorre el subárbol y rellena recursivamente los valores legales para cada campo de lista de selección secundario empleando la búsqueda primera de anchura, mire la saga de depGraphValueUpdater.js.

Felicitaciones, ¡no hay más que decir! Aunque es verdad que no pudimos hacer más sencilla la explicación de la creación de una interfaz de usuario para listas de selección dependientes, esperamos que no le resultara tan complicado.

Como repaso, para construir una interfaz de usuario para un editor de listas de selección dependientes, la API de la interfaz de usuario le da toda la información que necesita en tres lugares, en la propiedad objectInfo.dependentFields, en la propiedad objectInfo.fields[nombreCampo].controllingFields y en la respuesta del extremo /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}.

La API de la interfaz de usuario le da el poder y la flexibilidad para construir interfaces de usuario de Salesforce personalizadas que aportan felicidad a sus clientes. Esperamos que la aplicación Record Viewer le ponga en un buen puesto de salida. No dude en sacudir el repositorio GitHub y enviar solicitudes de extracción. ¡Esperamos saber algo pronto de sus aplicaciones!