New Lightning Component for Maps

New Lightning Component for Maps

20180126 UPDATE!! I got some new and very useful information about maps. Please, go to Latest Update section if you have already read this entry

A couple of years ago, I wrote a post about how to visualise maps and locations on Visualforce pages, and without having that in mind, Winter ’19 helped me to deliver a second part of that one. Now, Salesforce has released a new Map Lightning Component, so I will give it a try before moving to Spring ’19 version.

You will also find a similar example on Winter ’19 maintenance exam challenge. My code is much simpler than the maintenance one, so maybe it is a good start point before making it more complex.

In a similar way as the old post, I need an object with the information to show. In my case, Doctor object with a record about my Dentist, including a Geolocation field, the Location__c one.

Having that, I need to write some code in order to deliver a Lightning Component that call the standard Map component.

I will start with the Controller in Apex in order to retrieve the information from the database. Remember that Geolocation fields is a compound field with 2 pieces, Latitude__s and Longitude__s.

public with sharing class DoctorMapController
{
   @AuraEnabled
   public static List<Doctor__c> getDoctors()
   {
      List<Doctor__c> doctors = [Select Id,
                                   Name, 
                                   Address__c,
	                           Email__c, 
                                   Phone__c,
	                           SurgeryName__c, 
                                   Location__Latitude__s,
                                   Location__Longitude__s
                                 FROM Doctor__c];
      return doctors;
   }
}

We will continue with the component.

First of all, we implement usual interfaces in order to be able to visualise the component on different areas/views of our environment.

Secondly, I will add the tag <lightning:map> and its properties in order to show map information. The most important is mapMarkers. It is an array of markers that will help us to set the usual “pointer globe” icon that google maps provides. After that, we can add others like markerTitle or zoomLevel but they are not required, they just add more value to the component and the visualisation itself.

So having to provide information to mapMarkers and others, we need attributes in order to retrieve the data from our component Controller. That’s why we have 2 <aura:attribute> They are like variables on an apex class. Define the type and name of the variables that you will use among the component to get and set the data you need.

Finally <aura:handler> that will execute the action that will initialise the component via the javascript Controller. It is like the start point.

<aura:component implements="force:appHostable,
                            flexipage:availableForAllPageTypes,
                            flexipage:availableForRecordHome" 
                controller="DoctorMapController" 
                access="global" >
   <aura:attribute name="mapMarkers" type="Object" access="PRIVATE" />
   <aura:attribute name="markersTitle" type="String" access="PRIVATE" />
   <aura:handler name="init" value="{!this}" action="{!c.init}"/>
   <aura:if isTrue="{!!empty(v.mapMarkers)}" >
      <lightning:map 
		     mapMarkers="{!v.mapMarkers}" 
		     markersTitle="{!v.markersTitle}"
                     zoomLevel="5"
      />
   </aura:if>
</aura:component>

And the init, points to here, the javascript Controller. It depends on the way you develop. Actually you can add all method logics to here, but it is a good practice to have it on the Helper, and on the Controller, just a call to there.

({
   init : function(component, event, helper) {
	helper.initHelper(component, event, helper);
   }
})

Finally a Helper where we will find main methods. If you are familizarize with Javascript, it will not be very difficult to understand.

Basically the variable action, access our Apex Controller getDoctors method in order to retrieve all Doctor__c records.

With this information, we can iterate over there and show all doctors on our map. But my case is much simpler, I get the first one, and populate another variable with its information. Finally I will set the values to the component attributes, markTitle and mapMarkers.

But let me focus for a second on the variable that is assigned to mapMarkers attribute. The variable markers is an array because, as I mentioned at the beginning, mapMarkers parameter is an array with locations. But what do I need to specify there? The main one is Location that could be populated with the Geolocation field data or with a direction itself. After that we only have some more descriptive data like the title or the description. They add value to the map but the required one is Location.

({
   initHelper : function(component, event, helper) {
        helper.defineMarkers(component, event, helper);
   },
   defineMarkers : function(component, event, helper) {
	let action = component.get("c.getDoctors");
	action.setCallback(this, function(response) {
	   const data = response.getReturnValue();
	   const dataSize = data.length;
	   let markers = [];
        
           //I will only show my single record
	   const dentist = data[0]; 
	   markers.push({
                'location': {
                    'Latitude':dentist.Location__Latitude__s,
                    'Longitude':dentist.Location__Longitude__s
                },
                'title' : dentist.SurgeryName__c,
                'description' : 
                      dentist.SurgeryName__c + 
                      ' dentist Location at ' + 
                      dentist.Address__c
	   });
	   component.set('v.markersTitle', 
                         'Out and About Communications Dentist 
                         Locations');
	   component.set('v.mapMarkers', markers);
	});
	$A.enqueueAction(action);
   }
})

And here you can find the result, a map with the “Moyua Square” with a pointer to my dentist place.

Reading this entry you can realise that using <lightning:map> is very simple and that actually, I focused on explain how to build the component itself, but if you would like to know more or create more complex pieces of code, take a look at the documentation and do some searches on internet. I’m sure people share their experiences with the new <lightning:map>.

Latest Update:

After reading this entry, my friend Alba Rivas told me she was doing some researches on maps too, and found out some useful information that shared with me:

If an address is specified for a marker, instead of its latitude and longitude, the component makes API requests to the google geolocation, and per Google documentation, it seems it has a cost. Also I would like to know if there is an event that I can listen when a marker is selected.

The API key is from Salesforce, and there are no current limitations on its use. We are going to add a click handler for selecting a marker in an upcoming release

Alba Rivas question and Salesforce response

Visual Flow & Lightning Components (I)

Visual Flow & Lightning Components (I)

It’s a fact that Visual Flow needed something that could make it more attractive to developers, and Salesforce did it.

Now we are able to integrate a Lightning Component as part of the Flow with just drag and drop and this blog will explain how easy it is.

Please, take into account that if your org is on Spring ’19, the look and feel of the Cloud Flow designer would be different because Salesforce is going to deliver Flow Builder

Using Standard Lightning Component

When we add a new screen to the flow, the first action is to add some new fields to show them. If we click on “Add a Field” tab, we  can scroll down and under Extensions section, we can find Lightning Components label.

Captura de pantalla 2018-11-30 a las 20.30.40

After adding it to the screen, you need to highlight it, so that you would be able to see all Standard Lightning Components that Salesforce already provides and we can use.

My example already have 2 radio buttons in order to decide if I would like to go ahead with the booking process or I prefer to stop it.

Captura de pantalla 2018-11-30 a las 20.34.09.png

And I would like to show something nicer like a toggle, the one that I already selected.

Winter ’19 provides new Lightning Components we can add to flows. Toggle is one of them.

So the result would be like below image.

Using Custom Lightning Component

But what about if you don’t find the Lightning Component you really need on Flow? That is not a problem, because you can also create your custom Lightning Component and add it to the Flow.

Let’s talk about another example. We will create a simple Flow with 2 DatePickers. The first one is provided by Flow, however the second is a custom Lighting Component. How does it look like?

Captura de pantalla 2018-12-20 a las 9.46.35.png

Their look and feel are similar and both work like tweens. The only difference with the Lightning component is that we can customize it. For instance, you can see that the size is larger than the flow one. Or you can change background color for instance.

How can we visualize in the Flow? First of all, remember that the DatePicker is a custom Lighting Component, so yes, you need to be sure that you implement a new interface in order to make it visible: lightning:availableForFlowScreens

Captura de pantalla 2018-12-20 a las 13.07.27

So doing that, it will appear on flow with all standard lightning components that Salesforce already provide, so it is just a matter of drag and drop.

Captura de pantalla 2018-12-20 a las 13.17.47

And that’s all, you have integrate your custom Lightning Component into your Flow.

Now Flow is beautiful as well as useful

Visualforce y Lightning Experience

Después de un gran parón en mi blog debido a dreamOlé y otros eventos, hoy me gustaría publicar un post muy pero que muy pequeñín sobre una funcionalidad que nos trae la Summer ’18: visualizar una página Visualforce con estilo Lightning Experience.

Lo primero de todo, comencemos con la pregunta:

¿Trabajas en Lightning Experience?

Si, tu respuesta es NO … no eres el único, ya que una servidora sigue abriendo Classic UI casi siempre, pero chicos, hay que ser valientes y movernos a LEX !!!

Una de las razones por las que sigo en Classic es porque soy de la vieja escuela, y cuando tengo que montar una nueva UI me voy a Visualforce en vez de Lightning Components. Segundo error …. es hora de perder el miedo a Lightning Components y empezar a desarrollar UI de la forma que Salesforce nos aconseja.

¿Qué pasa si mi producto tiene ya cientos de páginas Visualforce? ¿Debo migrar todo a Lightning Components?

Teniendo en cuenta que el tiempo que puedes necesitar para ello puede ser elevado, y que desgraciadamente Lightning Components no ofrece toda la funcionalidad de Visualforce, yo migraría aquello que fuera sencillo. Por otro lado, si hay que hacer una nueva UI, revisad primero si Lightning Components os sirve.

¿Qué hacemos con el resto?

Salesforce responde a nuestras súplicas, y en mi caso, casi me echo a llorar cuando vi lo sencillo que era aplicar el estilo Lightning Experience a una página existente, de forma que pasamos de esto:

Captura de pantalla 2018-05-25 a las 11.44.33

A esto:

Captura de pantalla 2018-05-25 a las 11.45.42

¿Cómo lo hemos conseguido? Fácil.

Para ello necesitamos una Visualforce, que hemos rescatado de esta entrada, donde creábamos una vista alternativa a una cuenta, junto con su listado de oportunidades.

Una vez que tenemos nuestra página Visualforce, tenemos que asegurarnos que será visible en Lightning Experience, para lo que tenemos que marcar el checkbox “Available for Lightning Experience, Lightning Communities and the mobile app”

Captura de pantalla 2018-05-25 a las 11.43.51

Con esto conseguimos visualizar la página en Lightning Experience. Es decir, sin este checkbox, aunque usemos esta página para sobre-escribir la vista de un registro de Account, y la visualicemos en Classic UI, si nos movemos a LEX, visualizaremos la página Standard de Salesforce en LEX en vez de la nuestra.

Y por último (si un último paso!!) sólo tenemos que añadir lightningStyleSheets="true" al tag <apex:page> de forma que nuestra página quedaría así:

<apex:page standardController="Account" lightningStyleSheets="true">
 <apex:form >
  <apex:pageBlock title="Vista Cuenta">
   <apex:pageBlockButtons >
    <apex:commandButton value="Edit" action="{!edit}"/>
   </apex:pageBlockButtons>

   <apex:pageBlockSection title="Campos Cuenta" columns="2">
    <apex:outputField value="{!account.name}"/>
    <apex:outputField value="{!account.site}"/>
    <apex:outputField value="{!account.type}"/>
    <apex:outputField value="{!account.accountNumber}"/>
   </apex:pageBlockSection>
  </apex:pageBlock>
 </apex:form>
 <apex:relatedList list="Opportunities" />
</apex:page>

Si quieres saber más, échale un ojo a este link.

 

Picklist dinámicos en Lightning Components

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.

Captura de pantalla 2017-08-02 a las 18.28.06

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

Captura de pantalla 2017-08-03 a las 16.58.42

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.

Captura de pantalla 2017-08-03 a las 17.10.17

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.

Captura de pantalla 2017-08-03 a las 20.03.46.png

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.

London’s Calling ’17 and Visual Flow

Last 10th of February, I attended London’s Calling ’17 and I can say it was a great and funny day.

Captura de pantalla 2017-03-12 a las 19.22.17

And obviously, we had lot of opportunities to learn more about Salesforce. Actually I had the chance to run a session about Visual Flow where I got nice questions that I promised to review and answer, and here is the time for it:

1. Did you find any issue related to Communities?

When I created a new Community in my org and tried to add elements, I was not able to find the Flow Lightning Component that allows me to include visualflows in Lightning Experience. However, I can add visualforce pages, so if you have your flow embebed in a visualforce page and you set this page up to be used in Communities, then, it seems you can see your flow in the Community.

2. Did you find any issue during deployment?

Although my daily job tasks do not include deploying flows, I could say that those times that I tried I did not get any special issue to highlight. We just need to keep in mind few things, like also include those objects that flow references but this is like some other components. In addition, although in your org you have several versions, in change set you can only include the latest active one. And as I said in the session, in order to include a flow in package, it should be active, otherwise, it will not appear as an element to be included. You can find more information here.

3. What is coming in Spring ’17 release?

I talked about some Spring’17 features, but I also promised to review, the new FLS feature. But let start checking the behaviour if I do not have access to the object itself.

So having a user which profile only allows to Read and View All reservations:

Captura de pantalla 2017-03-12 a las 11.11.03.png

if I try to run the BookingSystem application, by the time I try to save the Reservation, I get an unhandled error (that’s why Salesforce encourage to cover these in the same way we do in apex) And the administrator receives an email with the log and the error:

Captura de pantalla 2017-03-12 a las 11.14.24Captura de pantalla 2017-03-12 a las 11.15.04

Let’s double check new FLS for Flow in Spring’17

Captura de pantalla 2017-03-12 a las 12.06.57

In this scenario, I have Filter Inaccessible Fields from Flow Request enabled in Process Automation Settings, and also, I have defined a Field Level Security of No Visible for Facility Price in my custom profile

Captura de pantalla 2017-03-12 a las 11.28.42.png

So, having this configuration, I have created also this flow in order to insert a new Facility via a Fast Create screen

Captura de pantalla 2017-03-12 a las 19.01.51.png

So in order to do that, I create a SObject and I use the input fields from the first screen in order to make an assignment to the SObject fields and finally pass the SObject to the Fast Create

Now, if I run the flow with the profile that doesn’t have price visibility, I can create a new record, but having an empty price.

Captura de pantalla 2017-03-12 a las 19.11.19.png

However, if I disable Filter Inaccessible Fields from Flow Request enabled in Process Automation Settings, and run the flow again, the result is different. The record is not half created and the administrator receives and email with the error.

So now, what should I do? Up to you really. You can handle the exception and provide a nice error message and don’t allow the end user to create a Facility record without the prices. However, if the field is not required for the business, you can leave this half creation.

Any other question about Visual Flow? Leave it as a comment and I will try to check it and reply you.

 

 

Lightning Experience and Visual Flow

Today I’m going to do some researches with one of these #clicksnotcode features that Salesforce provides in the Platform, Visual Flow. Ok, this is not a hot topic now, but if we want to check how it works in Lightning Experience (LEX from now onwards) then, this post could be a bit more interesting for you.

Take into account that Visual Flow in LEX is in beta

The use case it’s simple. I have a Hotel, and I would like to get some feedback from my guests, so I will send them a survey.

The Flow would be very simple, and as the creation is not the main goal here, I will not show it step by step. But if you are new, double check Visual Flow documentation and also TrailHead.

First of all we need to open Flow Designer, so, can I do it in LEX? Yes. Go to Setup and just write “flow” in order to find it and be able to open this platform feature.

captura-de-pantalla-2016-12-02-a-las-10-35-11

As you can see on below image, the Flow Designer looks like the Classic UI one, so you would not need to learn how to work with it if you already used it.

captura-de-pantalla-2016-12-02-a-las-12-08-29

Once I have created my Flow, we can add it to our Home page, but remember to Activate it, otherwise it would not be available in Lightning App Builder.

So, go to Home, Edit this page, and Lightning App Builder will help you to add it there.

How? Really simple, if you go to Standard Apps, one of them is Flow (beta) so you only need to click on it, and your flow will appear in the home page.

captura-de-pantalla-2016-12-02-a-las-10-57-11

But actually I have 2 active flows, so what can I do? When you highlight your custom Flow, on the right side you have the option to select the one you need to show, so just pick one.

captura-de-pantalla-2016-12-02-a-las-11-12-38

I can also say that I had to try a couple of times till I got this behavior. First time, Book Room flow did not appear on the drop down list, so there were not way to add it to my Home page. But remember, this feature is on beta, so be patience and try till you get it.

Last thing to mention is the fact that you can chose between 1 or 2 columns. My first thought was that this attribute would help me to determine if I wanted 2 flows on the same row on the page layout, but it is not related to that. It just help you to set all your flow fields in 2 columns instead of one. For instance, these 2 screens show same fields but they are sorted in a different way.

captura-de-pantalla-2016-12-02-a-las-12-11-41captura-de-pantalla-2016-12-02-a-las-12-11-08

And how it works? In Spanish we say better a image rather than thousand words but in this case, better a video rather than thousand images.

 

Maybe your last question is if we can add it to any other place? Yes, for instance, I can include it in my Guest record layout

captura-de-pantalla-2016-12-02-a-las-13-06-23

But let’s make up a little bit and define something for every guest:

captura-de-pantalla-2016-12-04-a-las-20-20-05

In order to do that, I had to add a Lookup step in the Flow and use the variable {!recordId} as a filter. So that, I can retrieve Guest object record and use it in my Flow.

captura-de-pantalla-2016-12-04-a-las-20-26-44

It was not easy to find the way to retrieve the recordId but this post helped me a lot.

I hope you liked this short entry about Flow. You can find more information on Salesforce release notes entry.

 

 

 

Lightning Connect – Visualize your external data in Salesforce easily

By Christmas 2013, I received a fantastic present. FinancialForce.com platform team was starting to take a look at one of the new pilots that Salesforce had. By that time we called it “External Data Source” or “External Objects” however, nowadays you know it as Lightning Connect.

And here I am, writing about this feature that was released during Dreamforce ’14 and after having talked about it on such amazing event, Madrid ’15, London ’15 and finally few weeks ago at Dreamforce ’15, now, it’s time to share my knowledge via a blog entry.

So first of all, what is Lightning Connect? This feature allows you to link an external repository, Google Drive, SharePoint, Amazon and others, to your Salesforce organization, therefore, you can visualize and use the data that you have outside of your org. At this moment maybe you are thinking about an ETL (Extract – Transform – Load) but Salesforce already have features like Apex Data Loader. So keep in mind. This is not an ETL.

How does it work? The platform makes http requests to the External Repository and its information is sent back to the org in Real Time via an OData protocol.

Captura de pantalla 2015-05-23 a las 19.33.18

It could sound too technical or complicated if you are not a developer, even it could make you give up and think that Data Import Wizard will help you to solve your scenario. So let me show you a comparation of what you would need to do if you use these tools.

Data Import Wizard vs Lightning Connect

Data Import Wizard

  1. Create Custom object and / or fields
  2. Save data in a csv file
  3. Launch Import Wizard
    1. Choose the data
      1. What do I need? Create or Update
      2. Select the csv file
    2. Edit mapping
    3. Start Import

If you need to modify a record, you would have to run all sub steps that belongs to point 3.

Lightning Connect

  1. Create the connection
  2. Synchronize metadata that means create automatically the object and its fields
  3. Create a Tab
  4. Refresh the List View

Any time you need to make a modification outside of your organization, you should only need to refresh the page to see the changes in Salesforce.

Checa Hotel – Use Case

Sometimes it is easier to understand things when we see a real use case. So lets go for it.

Checa Hotel saves its reservations in a spreadsheet:

Captura de pantalla 2015-10-03 a las 18.53.03

But by the time they started moving into Salesforce, they continued using Google Drive for a while. In any case they wanted to avoid the double effort that means add reservations to the google spreadsheet and move data into Salesforce every time that a guest make a new reservation.

How can Lightning Connect help?

This Salesforce feature will help us to show in our organization any change in the Google Spreadsheet. How?

Lightning Connect is a feature that via OData protocol help us to link the external repository with your Salesforce organization. Per connection, you can create a single External Data Source that will have 1 or many External Objects. At the end this External Object is the metadata that we are going to use to show the data that is stored in the Google Spreadsheet in our case.

Captura de pantalla 2015-10-03 a las 19.00.55

What do we need to get it? We only need to follow above steps.

1. Create a Connection

1.1. Get the URL

There are different ways to get this url. At the end we need to create the connection we have explained above. And we can do it with an existing OData provider, like JitterBit, or we can create something with code. This is my case, where I have used Heroku in order to create an application in Java that is able to read a google sheet via its API.

As a start point you can check odata4j. Actually, my Java application started with some code that I pasted from this page.

So basically you only need to create your producer and register some data there. Bellow example it will just show numbers from 0 to 20.

//InMemoryProducer is a readonly odata provider that serves up POJOs 
//as entities using bean properties
//call InMemoryProducer.register to declare a new entity-set, 
//providing a entity source function and a propertyname to serve as the key
final InMemoryProducer producer = new InMemoryProducer("InMemoryProducerExample", 1000);
		
//expose an large list of integers as an entity-set called "Integers"
producer.register(Integer.class, 
                  Integer.class, 
                  "Integers", 
                  new Func<Iterable>()
                  {
			public Iterable apply()
			{
                          return Enumerable.range(0, 20);
			}
		  }, 
                  Funcs.method(Integer.class, Integer.class, "intValue"));

So we need to do something similar but with the data that we read from google spreadsheet.

In order to do that, the first thing is the creation of an inner class where we will store the data

public static class Reservation
{
   private Integer externalId;
   private Integer roomNumber;
   private Date startDate;
   private Date endDate;
   private Double price;
   private Boolean paid;
   private String guestName;
   private String specialRequirement;
		
   public Integer getExternalId(){ return externalId; }
   public void setExternalId(Integer value) { externalId = value; }
  
   ... 
   //getter and setter are required to be able to read info in Salesforce
}

The next step is to have a method that reads the data

public static List readGoogleReservations(SpreadsheetService googleService) 
{
   List reservationInfo = new ArrayList();
   URL feedUrl = null;
   feedUrl = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
   ...
   return reservationInfo;
} 	

And this method will be called from my producer:

producer.register(Reservation.class,
 		  "HotelReservation",
	 	  new Func<Iterable>()
	 	  {
	 	     public Iterable apply()
	 	     {
                        try
                        {
	 		   return readGoogleReservations(googleServicefinal);
                        } 
                        catch (Exception e) { return null; }
                     }
	 	   },
                  "ExternalId");

1.2. Create the External Data Source

Once you have your Heroku application and the url that it returns, the rest is just #clicksnotcode.

You only need to go to External Data Source entry on the left side bar menu under Setup:

Captura de pantalla 2015-10-03 a las 20.13.39

And click on New. Then just enter information about the new Connection.

Captura de pantalla 2015-10-03 a las 20.09.18

As you can see on above image, it is really simple. Just provide the Name and Type chose OData 2.0 that is the one we have used in our Heroku application.

Then, populate the Url field with the application one, that looks like

http://<funnyname_provided_by_heroku>.herokuapp.com/<yourProducer>.svc

And that is all. Just save, and your connection will be created.

In the case that you are using a different way to create the connection, you would only need to select a different type from the drop down list. For instance on below image, we can also have the Salesforce option, just a Simple URL, and the Custom one that is called LoopbackDataSourceProvider. This Custom one is related to a Custom Apex Connector, another interesting topic that I will talk about in a future entry.

Captura de pantalla 2015-10-03 a las 20.10.04

2. Synchronize Metadata – Create External Object

The next step is the creation of the Metadata that will contain the information we want to show. Even we can use the External Object entry that we can find on the left side bar menu, and create it manually, my advice is to use the External Data Source and create it automatically. Just clicking on Validate and Sync button, it will read those objects that the connection provides and create them for you.

Captura de pantalla 2015-10-03 a las 20.09.51

And if we open HotelReservation one, we can check that it looks like a custom object.

Captura de pantalla 2015-10-03 a las 20.08.58

3. Create a new Tab

Finally I want to see the data in my organization, so I will just create a Tab for it and update the look and feel of the list view.

Captura de pantalla 2015-10-03 a las 20.10.38

Also, if I want to open any of the records, we will see the data as well.

Captura de pantalla 2015-10-03 a las 20.12.08

And that’s all, we have our Google Spreadsheet links to our Salesforce organization.

4. Refresh the List View

Something that I have mentioned since the very beginning is that one of the advantages of Lightning Connect is that any change outside will be reflected in Real Time in my org. And the best way to show it is with a video.

Hightlights

I would like to finish this entry with some bullets points that could be interesting for you

  • Limitations
    • Lightning Connect has an additional cost. How much? Please contact Salesforce.com for more information.
    • We can only have 100 external objects per org
    • It is Read Only for now, although Winter’16 brings the writable feature. Let see how it works in a future entry 🙂
    • Callouts: At the end we are making calls to google drive, so this limitation is still in place. But if you need to increase it, contact Salesforce because this is a soft limitation.
    • External Objects don’t support Master-Detail relationships but Lightning Connect offers workaround for that. Follow next entries where I will talk about what you can do with an External Object.
    • It doesn’t provide Reports for now.
  • Advantages
    • Once you have the Url, the configuration is #clicksnotcode
    • It provides to end user Real Time refresh so if you are worried about performance, just keep in mind that the first time you open your External Object tab, it will take a bit longer because it is doing the connection. But from that moment, any update will be as fast as your internet connection allows you.
    • Finally, something that can make you think that pay for it, worth it. It doesn’t count against the storage limitation because we are not saving anything in Salesforce. We are just showing what we have outside. It is like a mirror.

Captura de pantalla 2015-10-03 a las 20.51.19

I hope now you can understand a bit better Lightning Connect and all its benefits.

Enjoy and see you in my next entry.