Erstellen einer Benutzeroberfläche für abhängige Auswahllisten
Lernziele
- Absetzen einer Anforderung an die User Interface API, um alle Auswahllistenwerte für einen Datensatztyp abzurufen
- Nennen der Eigenschaft, die eine Zuordnung der abhängigen Felder eines Objekts enthält
- Nennen der Eigenschaft, die eine Liste der Steuerfelder eines Felds enthält
Was ist so besonders an abhängigen Auswahllisten?
Es ist schwierig, eine Benutzeroberfläche für abhängige Auswahllistenfelder zu erstellen, doch mit der User Interface API ist es einfacher. Die Werte eines abhängigen Auswahllistenfelds werden auf der Grundlage einer Auswahl in einer anderen Auswahlliste oder einem Kontrollkästchen (dem so genannten Steuerfeld) gefiltert. Sehen Sie sich diesen Screenshot des abhängigen Auswahllisten-Editors in der Record Viewer-Anwendung an. Eine Auswahlliste "Country" steuert eine Auswahlliste "State/Province" (die von der Auswahlliste "Country" abhängt). Wenn ein Benutzer einen "Country"-Wert auswählt, werden die "State/Province"-Werte entsprechend gefiltert. Wenn ein Benutzer einen "State/Province"-Wert auswählt, bewirkt diese Auswahl die Filterung der "City"-Werte.
Sie verstehen sicherlich die Grundidee dabei: Abhängige Auswahllisten beschleunigen das Ausfüllen eines Formulars, da die Werte gefiltert werden, so dass Sie weniger Zeit mit Scrollen verbringen. Und ein abhängiger Auswahllisten-Editor erleichtert die Bearbeitung zugehöriger Felder, besonders auf Mobilgeräten. Stellen Sie sich vor, wie schwierig es sein könnte, von Feld zu Feld zu scrollen, wenn sich abhängige Werte ändern. Es ist viel einfacher, alle zugehörigen Auswahllistenwerte in einem Dialogfeld zu ändern.
Aber genau diese Beziehungen zwischen den Steuerfeldern und den abhängigen Feldern sind es, die das Erstellen einer Benutzeroberfläche erschweren. Eine Auswahlliste (oder ein Kontrollkästchen) kann mehrere abhängige Auswahllisten steuern. Und eine abhängige Auswahllisten wird gesteuert, kann aber auch selbst steuern. Tatsächlich kann eine abhängige Auswahlliste mehrere andere abhängige Auswahllisten steuern. Die Hierarchie abhängiger Auswahllistenfelder hat eine Baumstruktur. Und ein Objekt kann einen ganzen Wald aus Bäumen besitzen.
- objectInfo.dependentFields enthält eine Zuordnung der abhängigen Felder eines Objekts.
- objectInfo.fields[Feldname].controllingFields enthält eine Liste der Steuerfelder eines Felds.
GET /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}
Die Record Viewer-Anwendung nutzt diese Eigenschaften und die Ressource zum Erstellen ihres abhängigen Auswahllisten-Editors.
Verwenden des abhängigen Auswahllisten-Editors
Um den abhängigen Auswahllisten-Editor in der Record Viewer-Anwendung zu sehen zu bekommen, legen Sie eine abhängige Beziehung zwischen mindestens zwei Feldern eines Objekts an.
Erstellen Sie in Ihrem Trailhead Playground eine Feldabhängigkeit für das Account-Objekt. Anschließend bearbeiten wir einen Account in der Record Viewer-Anwendung, um den abhängigen Auswahllisten-Editor in Aktion zu sehen.
- Geben Sie in Setup den Begriff Objekt ein und wählen Sie Objekt-Manager aus.
- Wählen Sie aus.
- Klicken Sie auf Neu.
- Nun erstellen wir eine Beziehung zwischen zwei bestehenden Feldern. Wählen Sie Bewertung als Steuerfeld aus. Als abhängiges Feld wählen Sie Customer Priority aus. Klicken Sie auf Weiter.
- Bearbeiten Sie die Feldabhängigkeiten so, dass die Spalte "Hot" die Angabe "High", die Spalte "Warm" die Angaben "Low" und "Medium" und die Spalte "Cold" die Angabe "Low" enthalten. Klicken Sie auf Speichern.
- Öffnen Sie die Record Viewer-Anwendung und wählen Sie einen Account aus der Liste "Zuletzt verwendete Elemente".
- Klicken Sie auf Bearbeiten.
- Klicken Sie neben dem Feld "Accountbewertung" oder "Customer Priority" auf das Stiftsymbol.
Das Dialogfeld des abhängigen Auswahllisten-Editors wird geöffnet. Wählen Sie in diesem Dialogfeld Werte für beide Felder aus. Wenn Sie einen Wert für ein Feld auswählen, werden die Werte für das andere Feld auf Basis Ihrer Auswahl in Setup gefiltert.
Wie abhängige Auswahllisten in der User Interface API sichtbar gemacht werden
Abhängige Auswahllistenfelder sind Teil einer Abhängigkeits-Baumstruktur, also einer Hierarchie aus Steuerfeldern und abhängigen Feldern. Jeder Knoten in einer Baumstruktur kann über beliebig viele untergeordnete Knoten verfügen. Ein Knoten ohne untergeordnete Knoten wird Blattknoten genannt.
Unsere Abhängigkeiten-Beispielstruktur ist einfach, da sie nur ein Stammfeld, Rating, enthält, das ein Feld, CustomerPriority__c, steuert. Würde Rating ein weiteres Feld steuern, sähe unsere Abhängigkeitshierarchie langsam wie eine Baumstruktur aus. Doch mit nur zwei Feldern hat unsere Abhängigkeits-Baumstruktur keine Zweige, sondern nur den Stamm.
Ein Objekt kann über beliebig viele dieser Abhängigkeits-Baumstrukturen verfügen. Zum Erstellen einer Benutzeroberfläche, mit der Benutzer abhängige Auswahllisten bearbeiten können, müssen Sie die komplette Hierarchie jeder Baumstruktur kennen.
Das Account-Objekt könnte beispielsweise zwei Abhängigkeits-Baumstrukturen haben, einen mit dem Stammfeld "Land" und eine mit dem Stammfeld "Bewertung". Die Abhängigkeits-Baumstrukturen aus unseren Beispielen sind einfach, da jedes Steuerfeld nur ein abhängiges Feld steuert. Sie können sich jedoch sicherlich vorstellen, dass das Feld "Country" nicht nur das Feld "State/Province", sondern auch noch ein Feld "Language" und ein Feld "Time Zone" steuern könnte.
Wie Sie bereits wissen, gibt es in der User Interface API zwei Antworteigenschaften, die die Feldabhängigkeitsstruktur bereitstellen. Diese beiden Eigenschaften sind in der Nutzlast objectInfo enthalten, die Teil der Antwort von /ui-api/record-ui/{recordIds} ist. Zum Anzeigen der JSON-Antwort öffnen Sie ein Account-Objekt in der Record Viewer-Anwendung und klicken auf Show JSON.
- objectInfo.dependentFields
- Diese Eigenschaft stellt die Baustrukturen für abhängige Felder eines Objekts bereit. Diese JSON-Beispielantwort enthält nur ein Stammfeld, Rating, ein Objekt kann jedoch mehrere Stammfelder haben.
"objectInfos" : { "Account" : { ... "dependentFields" : { "Rating" : { "CustomerPriority__c" : { } } }, ...
- Weist ein Objekt eine erweiterte Hierarchie abhängiger Felder auf, wie in unserem Beispiel mit "Country", "State/Province" und "City", verschachtelt die Eigenschaft dependentFields diese, bis sie einen Blattknoten wie "City" erreicht.
"objectInfos" : { "Account" : { ... "dependentFields": { "Country__c": { "StateProvince__c": { "City__c": {} } } }, ...
- objectInfo.fields[Feldname].controllingFields
- Diese Eigenschaft liefert die Liste der Felder, die dieses spezielle Feld steuern, ausgehend von dem Feld, das dieses Feld unmittelbar steuert. In unserem einfachen Beispiel gibt es für das Feld CustomerPriority__c ein Steuerfeld: Rating.
"objectInfos" : { "Account" : { ... "fields" : { ... "CustomerPriority__c" : { "apiName" : "CustomerPriority__c", ... "controllingFields" : [ "Rating" ], ...
- Weist ein Objekt eine erweiterte Hierarchie aus Steuerfeldern auf, wie in unserem Beispiel mit "Country", "State/Province" und "City", listet die Eigenschaft controllingFields diese ausgehend vom unmittelbaren Steuerfeld in der Reihenfolge ihres Auftretens auf.
"objectInfos" : { "Account" : { ... "fields" : { ... "City__c" : { "apiName" : "City__c", ... "controllingFields" : [ "StateProvince__c", "Country__c"], ...
GET /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}
"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" } ] },
Die Eigenschaft controllerValues liefert die Auswahllistenwerte und Indizes des unmittelbaren Steuerfelds. In unserem Beispiel ist das Feld "Customer Priority" und das unmittelbare Steuerfeld ist "Rating". Die Werte für "Rating" sind Hot, Warm, und Cold und die Indizes 0, 1 und 2.
Für jeden "Customer Priority"-Wert (High, Low und Medium) enthält die Eigenschaft validFor die "Rating"-Werte, die bei Auswahl aus einer Auswahlliste den "Customer Priority"-Wert enthalten, anstatt ihn herauszufiltern.
Lautet der "Rating"-Wert beispielsweise Hot (0), wird die "Customer Priority"-Auswahlliste so gefiltert, dass sie nur den Wert High enthält. Lautet der "Rating"-Wert Warm (1), wird die "Customer Priority"-Auswahlliste so gefiltert, dass sie die Werte Low und Medium enthält. Bei einem "Rating"-Wert von Cold (2), wird die "Customer Priority"-Auswahlliste so gefiltert, dass sie nur den Wert Low enthält.
Wir wissen jetzt, welche Informationen wir benötigen und wie wir sie aus der User Interface API abrufen. Als Nächstes schauen wir uns an, wie die Record Viewer-Anwendung ihren abhängigen Auswahllisten-Editor erstellt.
Abrufen der Auswahllistenwerte
Wenn ein Benutzer auf Create, Clone oder Edit klickt, um einen Datensatz zu erstellen, zu duplizieren oder zu bearbeiten, ruft die Record Viewer-Anwendung die Auswahllistenwerte für alle Felder im Objekt für den angegebenen Datensatztyp ab.
Sie können sich den Code in RecordViewerWrapper.js ansehen. Suchen Sie nach einem Aufruf von dispatch(actions.fetchPicklists(creds, apiName, recordType)) für die einzelnen Szenarien: onNewRecordClick, onCloneClick und onEditClick.
/* /sagas/picklistFetcher.js */ let url = action.creds.instanceUrl + '/services/data/v48.0/ui-api/object-info/' + action.apiName + '/picklist-values/' + action.recordType + '/';
/* /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
Anzeigen des abhängigen Auswahllisten-Editors
Wenn sich ein Datensatz im Edit-Modus befindet, zeigt die Anwendung neben abhängigen Feldern ein Stiftsymbol an, das angibt, dass der Benutzer den abhängigen Auswahllisten-Editor öffnen kann.
Wenn der Benutzer auf das Stiftsymbol klickt, öffnet die Anwendung den abhängigen Auswahllisten-Editor als modales Dialogfeld. Im Code wird der abhängige Auswahllisten-Editor als DepGraphEditor-Komponente bezeichnet.
/* /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> ); }
Zum Öffnen des modalen Dialogfelds ruft die Methode onClick() die Methode onEditDepGraph() auf, die die Aktion EDIT_DEP_GRAPH sendet, die den Modus in EditDepGraph ändern. Das modale Dialogfeld wird geöffnet, wenn der Modus geändert wird.
/* /reducers/record.js */ case 'EDIT_DEP_GRAPH': return { ...state, prevMode: action.prevMode, // save previous mode to return to on close mode: 'EditDepGraph' }
/* /components/Record.js */ <div> {uiMode === 'EditDepGraph' && <DepGraphEditor depGraph={depGraph} picklists={picklists} editValues={recordView.editValues} onFieldValueUpdate={onDepGraphFieldValueUpdate} onClose={() => onDepGraphClose(prevMode)}/> }
Aktualisieren der Auswahllistenwerte
Wenn ein Benutzer einen neuen Wert für ein Auswahllistenfeld auswählt, muss die Anwendung die Werte seiner untergeordneten Auswahllistenfelder aktualisieren. Und wenn diese untergeordneten Auswahllistenfelder wiederum über untergeordnete Auswahllistenfelder verfügen, muss die Anwendung auch diese aktualisieren, bis hinunter zu den Auswahllistenfeldern im Blattknoten, zu denen es keine untergeordneten Auswahllistenfelder mehr gibt. Um diese Aktualisierungen durchzuführen, ist es wichtig, die Abhängigkeits-Unterstruktur unter dem Feld zu kennen, dessen Wert sich geändert hat.
Wenn der vorausgewählte Wert für ein Feld weiterhin gültig bleibt, muss die Anwendung diesen Wert beibehalten.
/* /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.
Aus dieser extrahierten Unterstruktur und den Auswahllistenwerten, die abgerufen wurden, als der Benutzer auf Create, Clone oder Edit geklickt hat, um einen Datensatz zu erstellen, zu duplizieren oder zu bearbeiten, stellen wir die zulässigen Auswahllistenwerte für jedes Auswahllistenfeld in der Unterstruktur zusammen.
Im Abschnitt getLegalValues() von /helpers/depGraphHelper.js können Sie sich ansehen, wie die Anwendung die zulässigen Auswahllistenwerte für ein bestimmtes Feld abruft. Wenn Sie sehen möchten, wie der Code die Unterstruktur durchläuft und die zulässigen Werte für jedes untergeordnete Auswahllistenfeld rekursiv mithilfe einer breitangelegten ersten Suche auffüllt, schauen Sie sich die Saga depGraphValueUpdater.js an.
Herzlichen Glückwunsch! Sie haben es geschafft! Wir geben zu, dass wir das Erstellen einer Benutzeroberfläche für abhängige Auswahllisten nicht einfacher machen konnten als das Schneiden von Brot, hoffen aber, dass es einfacher ist als die Zubereitung eines Schokoladensoufflés und genauso lecker.
Kurze Zusammenfassung: Zur Erstellung der Benutzeroberfläche für einen abhängigen Auswahllisten-Editor stellt Ihnen die User Interface API alle erforderlichen Informationen an drei Stellen zur Verfügung, nämlich in der Eigenschaft objectInfo.dependentFields, in der Eigenschaft objectInfo.fields[Feldname].controllingFields und in der Antwort des Endpunkts /ui-api/object-info/{objectApiName}/picklist-values/{recordTypeId}.
Die User Interface API gibt Ihnen die Möglichkeiten und die Flexibilität, benutzerdefinierte Salesforce-Benutzeroberflächen zu erstellen, die Ihre Kunden begeistern. Wir hoffen, die Record Viewer-Anwendung ermöglicht Ihnen einen guten Einstieg. Scheuen Sie sich nicht, eigene Verzweigungen zum GitHub-Repo hinzuzufügen und Pull-Anforderungen abzusetzen. Wir freuen uns darauf, mehr über Ihre Anwendungen zu erfahren!