Mis últimos post técnicos en Español eran bastante básicos. Explicaba cómo crear páginas visualforce, controllers, etc. Sin embargo, desde entonces, Lightning Experience y Lightning Components han cogido el protagonismo en Salesforce.
Así que en esta entrada voy a explicar como crear una lista desplegable y poder determinar su valor por defecto desde App Builder, de forma que cada usuario puede decidir qué valor le conviene.
Caso de Uso:
Como usuario, me gustaría tener un lightning component que me muestre los eventos de Salesforce a partir de una ciudad elegida y decidir el valor por defecto de esa lista desplegable.
Pasos:
Lista desplegable con valores fijos
Comencemos creando un componente con determinadas ciudades pero sus valores son fijos. Para poder testearlo, lo mostraré en la pestaña Home de mi App Salesforce Events.
Para no alargar el post pondré una breve explicación de lo más importante en el código
SalesforceEventsComponent.cmp
<aura:component controller="SalesforceEventsController" implements="flexipage:availableForAllPageTypes" access="global"> <!-- Lista de ciudades sobre la que iterar --> <aura:attribute name="cities" type="List" /> <!-- Valor seleccionado para poder usarlo más adelante.--> <!-- Indicamos que el valor por defecto es "Madrid" mediante hardcode --> <aura:attribute name="selectedValue" type="String" default="Madrid"/> <!-- handler donde llamamos al método que me devuelve los valores de la lista --> <aura:handler name="init" value="{!this}" action="{!c.loadCities}" /> <!-- Usando Lightning Design System, creamos la lista desplegable --> <lightning:select name="mySelect" label="Select a city:" aura:id="mySelect" value="{!v.selectedValue}"> <aura:iteration items="{!v.cities}" var="theCity"> <option text="{!theCity}" value="{!theCity}" selected="{!theCity}"/> </aura:iteration> </lightning:select> </aura:component>
SalesforceEventsComponentController.js
({ loadCities: function (component, event, helper) { //asigno a la variable cities de mi component los nombres de las ciudades component.set('v.cities',['London','Madrid','Paris','San Francisco']); } })
Lista desplegable con valores dinámicos
El siguiente paso es recuperar de la base de datos los valores de la ciudades que tenemos. Según la imagen de más arriba, de la lista desplegable desaparecería San Francisco.
Para ello modificaría el Controlador JavaScript que llamaría a un Controlador en Apex dónde hacemos la búsqueda en base de datos.
SalesforceEventsController.cls
//Esta clase puede ser también global public with sharing class SalesforceEventsController { //Recuerda añadir la anotación @AuraEnabled //para el que método sea visible en el componente @AuraEnabled public static List<String> getCityNames() { List<String> cityNames = new List<String>(); //TODO: Recuerda SOC por lo que una SOQL debería hacerse en otra clase //destinada a ello. for(City__c c : [Select Id, Name From City__c]) { cityNames.add(c.Name); } return cityNames; } }
SalesforceEventsComponentController.js
({ loadCities: function (component, event, helper) { var action = component.get("c.getCityNames"); //asigno a la variable cities de mi component //los nombres de las ciudades que he recuperado del controlador apex action.setCallback(this, function(a){ component.set("v.cities", a.getReturnValue()); }); $A.enqueueAction(action); } })
Lista desplegable con valores dinámicos y Tabla con información
El siguiente paso es mostrar los valores de los eventos de Salesforce a partir de una selección en la lista desplegable.
SalesforceEventsController.cls
@AuraEnabled public static List<SalesforceEvent__c> getEventsByCity(String cityName) { City__c theCity = [Select Id From City__c Where Name = :cityName Limit 1]; Id theCityId = theCity.Id; List<SalesforceEvent__c> sEvents = new List<SalesforceEvent__c>(); //TODO: Recuerda SOC por lo que una SOQL debería hacerse en otra clase //destinada a ello. for(SalesforceEvent__c e : [Select Id,EventName__c,EventDate__c From SalesforceEvent__c Where City__c = :theCityId]) { sEvents.add(e); } return sEvents; }
En este punto me di cuenta que el refresco iba a ser más complicado de lo que tenía pensado ya que tuve que crear un componente hijo para la parte de los Salesforce Events, y un evento para poder hacer el refresco. Os dejo el enlace que usé como referencia.
SalesforceEventsEvent.evt
<aura:event type="APPLICATION"> <!-- A partir de una ciudad seleccionada, calculo los eventos --> <aura:attribute name="citySelected" type="String" /> </aura:event>
SalesforceEventsComponent.cmp
<aura:component controller="SalesforceEventsController" implements="flexipage:availableForAllPageTypes" access="global"> <!-- Attributes set on Design Page--> <aura:attribute access="global" name="headerText" type="String" /> <aura:attribute access="global" name="defaultCity" type="String" /> <aura:attribute name="cities" type="List" /> <aura:attribute name="selectedValue" type="String" default="Madrid"/> <aura:handler name="init" value="{!this}" action="{!c.loadCities}" /> <!-- onchange no funciona sin un handler relacionado con value el tag option --> <aura:handler name="change" value="{!v.value}" action="{!c.cityChange}"/> <!-- Usaremos onChange para llamar a la función js que refrescará la lista --> <lightning:select name="mySelect" label="Select a city:" aura:id="mySelect" value="selectedValue" onchange="{!c.cityChange}"> <aura:iteration items="{!v.cities}" var="theCity"> <option text="{!theCity}" value="{!theCity}" selected="{!theCity}"/> </aura:iteration> </lightning:select> <h1>Salesforce Events:</h1> <!-- Registro el evento y llamo al nuevo componente hijo para listar los eventos de Salesforce --> <aura:registerEvent name="appEvent" type="c:SalesforceEventsEvent"/> <c:SalesforEventList /> </aura:component>
SalesforceEventsComponentController.js
({ loadCities: function (component, event, helper) { var action = component.get("c.getCityNames"); action.setCallback(this, function(a) { component.set("v.cities", a.getReturnValue()); }); $A.enqueueAction(action); }, cityChange : function(component, event) { //Determino el nuevo valor por defecto que es el elegido component.set('v.selectedValue', component.find("mySelect").get("v.value")); component.find("mySelect").get("v.value"); //Paso al atributo citySelected del event el nuevo valor seleccionado var appEvent = $A.get("e.c:SalesforceEventsEvent"); appEvent.setParams({"citySelected" : component.get("v.selectedValue")}); appEvent.fire(); } })
SalesforEventList.cmp
<aura:component controller="SalesforceEventsController" implements="flexipage:availableForAllPageTypes" access="global"> <aura:handler event="c:SalesforceEventsEvent" action="{!c.loadEvents}"/> <aura:attribute name="cityToFilter" type="String" /> <aura:attribute name="sevents" type="SalesforceEvent__c[]" /> <aura:iteration items="{!v.sevents}" var="theEvent"> <li>{!theEvent.EventDate__c} - {!theEvent.EventName__c }</li> </aura:iteration> </aura:component>
SalesforEventList.cmp
({ loadEvents : function(component, event) { var citySentFromEvent = event.getParam("citySelected"); component.set("v.cityToFilter", citySentFromEvent); var action = component.get("c.getEventsByCity"); action.setParams({ cityName : component.get("v.cityToFilter") }); action.setCallback(this, function(a) { component.set("v.sevents", a.getReturnValue()); }); $A.enqueueAction(action); } })
Determinar un valor por defecto desde App Builder
La idea aquí es dar la opción de poner el valor por defecto cómo parte de la configuración. Para ello usaremos la funcionalidad Dynamic PickList for Lightning Components.
Añadiremos 2 atributos a nuestro componente. Uno sera un texto con información y el segundo nuestra lista desplegable.
SalesforceEventsComponent.cmp
<aura:component controller="SalesforceEventsController" implements="flexipage:availableForAllPageTypes" access="global"> <!-- Atributos para la Design Page--> <!-- Este primer atributo no es requerido para la lista desplegable --> <aura:attribute access="global" name="headerText" type="String" /> <!-- En vez de tener el valor por defecto con hardcode, usamos el atributo defaultCity --> <aura:attribute name="selectedValue" type="String" default="{!v.defaultCity}"/> <aura:attribute name="cities" type="List" /> <aura:attribute name="selectedValue" type="String"/> <aura:handler name="init" value="{!this}" action="{!c.loadCities}" /> <!-- Utilizo como valor para value el atributo defaultCity --> <lightning:select name="mySelect" label="Select a city:" aura:id="mySelect" value="selectedValue"> <aura:iteration items="{!v.cities}" var="theCity"> <option text="{!theCity}" value="{!theCity}" selected="{!theCity}"/> </aura:iteration> </lightning:select> ... </aura:component>
A continuación debemos crear nuestro design para añadir los nuevos campos a la Design Page.
SalesforceEventsComponent.design
<design:component label="Salesforce Events Cities"> <design:attribute name="headerText" label="Header Text" description="Text that will appear in the header when the component is displayed" default="City" /> <!-- datasource nos ayuda a lincar el atributo con una clase apex de dónde extraeremos la información que necesitamos --> <design:attribute name="defaultCity" label="Default City PickList" datasource="apex://SalesforceEventsCityValuePickListDefault" /> </design:component>
Por último, debemos crear la clase apex lincada a nuestro atributo.
Aunque en la documentación aparece como global, si la hacemos pública funciona igualmente.
Esta clase extiende VisualEditor.DynamicPickList y aunque tiene cuatro métodos para sobre escribir, sólo son requeridos dos. Uno determina el valor por defecto en la lista desplegable, y el otro nos muestra los valores.
SalesforceEventsCityValuePickListDefault.cls
public class SalesforceEventsCityValuePickListDefault extends VisualEditor.DynamicPickList { public override VisualEditor.DataRow getDefaultValue() { VisualEditor.DataRow defaultValue = new VisualEditor.DataRow('defaultLabel', 'defaultValue'); return defaultValue; } public override VisualEditor.DynamicPickListRows getValues() { VisualEditor.DataRow defaultRow = new VisualEditor.DataRow('defaultLabel', 'defaultValue'); VisualEditor.DynamicPickListRows myRows = new VisualEditor.DynamicPickListRows(); myRows.addRow(defaultRow); List<String> cities = SalesforceEventsController.getCityNames(); for(Integer i=0; i<cities.size(); i++) { VisualEditor.DataRow newRow = new VisualEditor.DataRow(cities.get(i), cities.get(i)); myRows.addRow(newRow); } return myRows; } }
Como muestro en la imagen de abajo, si abrimos el App Builder y seleccionamos nuestro Lightning Component, veremos los dos atributos que definimos en SalesforceEventsComponent.design siendo uno de ellos la lista desplegable con los valores que hemos recuperado de base de datos.
Un último apunte antes de terminar el post. No estoy cubriendo error handling en mi código pero échale un vistazo a la ayuda de Salesforce para ello. Por ejemplo este y este.
Como has podido comprobar, es muy sencillo. Si eres nuevo en este tema, los pasos son fáciles de seguir. Si ya eres un experto, y has encontrado algo en el código que se puede mejorar. Dímelo y así otros podrán beneficiarse de tus conocimientos también.