Mi primera Visualforce II

En la última entrada en Español estuvimos aprendiendo como empezar con Apex y Visualforce. Mostramos una nueva página con algunos campos del objeto Cuenta (Account). Hoy veremos cómo pasar de campos de lectura a escritura.

Lo primero de todo, intentaremos cambiar los campos que teníamos en la pantalla anterior a modo escritura. Para ello lo único que tenemos que hacer es utilizar un tag diferente: apex:outputField

En este caso, la página se llamará accountedit ya que la vamos a utilizar para modificar la información que ofrece.

<!-- accountedit -->
<apex:page standardController="Account">
  <apex:form >
    <apex:pageBlock title="Vista Cuenta">
      <apex:pageBlockSection title="Campos Cuenta">
        <apex:inputField value="{!account.name}"/>
        <apex:inputField value="{!account.site}"/>
        <apex:inputField value="{!account.type}"/>
        <apex:inputField value="{!account.accountNumber}"/>
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

Si escribimos una Url similar a esta:

https://<instancia>salesforce.com/apex/accountedit

vemos como ahora tenemos unos cuadros de texto donde añadir la información.

Captura de pantalla 2015-11-27 a las 18.43.01

De igual forma que en la anterior entrada, si queremos ver la información de una cuenta en concreto para modificarla, debemos simplemente añadir el Id único de la misma, en la url.

Captura de pantalla 2015-11-27 a las 18.49.54.png

Si haces una búsqueda en Google y pones algo del tipo “añadir campo de escritura en mi visualforce” aparecerá otro tag que también podrías usar: apex:outputText

¿Cual es la diferencia entre ambos tag si el resultado es el mismo? La principal es que outputField va a respetar el tipo de campo que quieras mostrar, mientras que outputText añade eso, un texto. Veamos un ejemplo que es bastante claro.

<apex:page standardController="Account">
  <apex:form >
    <apex:pageBlock title="Vista Cuenta">
      <apex:pageBlockSection title="Campos Cuenta">
        <apex:outputText value="{!account.type}"/>
        <apex:outputField value="{!account.type}"/>
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

En el código de arriba vemos como queremos mostrar un campo de tipo picklist en nuestra página visualforce. Uno con outputField y el segundo es outputText. ¿Cual es el resultado?

Captura de pantalla 2015-11-27 a las 18.59.02.png

En campos de tipo lectura no podemos encontrarla ya que muestra simplemente el valor que hay almacenado en base de datos. Sin embargo en el caso de escritura, la diferencia es mayor

Captura de pantalla 2015-11-27 a las 18.59.50

Mientras que la en la primera opción muestra sólo un texto, el valor asociado a ese campo en el registro, en la segunda se puede desplegar el campo y visualizar todas las opciones que ofrece.

<apex:page standardController="Account">
  <apex:form >
    <apex:pageBlock title="Vista Cuenta">
      <apex:pageBlockSection title="Campos Cuenta">
        <apex:inputText value="{!account.type}"/>
        <apex:inputField value="{!account.type}"/>
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

Esto no significa que no debamos usar outputText ó inputText, simplemente que en determinados casos, es preferible ouputField / inputField, ya que proporcionamos una mejor experiencia al usuario final.

¿Intentamos añadir más información a nuestras primeras páginas Visualforce? Algo que también nos puede interesar es mostrar información de los objetos relacionados. De igual forma que la página standard de Cuenta muestra las Oportunidades que están relacionadas con esa Account, ahora queremos mostrar esas Oportunidades en nuestra página Visualforce. Para ello utilizaremos el tag: apex:relatedList

En este caso, el tag deberemos ponerlo fuera del tag apex:form y simplemente añadir el nombre API del objeto. A partir de ese momento, Salesforce se encarga de todo el trabajo por nosotros.

 <apex:page standardController="Account">
   <apex:form >
     <apex:pageBlock title="Vista Cuenta">
       <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>

Captura de pantalla 2015-11-27 a las 19.07.09.png

En el caso de que queramos tener la opción de modificar alguno de los valores des esta lista relacionada, necesitaremos añadir código Apex y un Controlador. Pero esto es algo que veremos en la próxima entrada. Hoy continuaremos sólo con acciones que podamos llevar a cabo con el Controlador Standard de los objetos.

Uno de los últimos pasos que vamos a dar hoy será guardar en base de datos las modificaciones que hacemos en uno de los campos. Para ello, añadiremos un botón a nuestra página mediante el tag: apex:commandbutton Revisa este tag porque es muy interesante, ya que con sus atributos puedes indicar si mostrar el botón solo en la parte de arriba de la pantalla ó en ambos lados (por defecto los muestra en los dos).

<!-- accountedit -->
<apex:page standardController="Account">
  <apex:form >
    <apex:pageBlock title="Vista Cuenta">
      <apex:pageBlockButtons >
        <apex:commandButton action="{!save}" value="Save"/>
      </apex:pageBlockButtons>
      <apex:pageBlockSection title="Campos Cuenta">
        <apex:inputField value="{!account.name}"/>
        <apex:inputField value="{!account.site}"/>
        <apex:inputField value="{!account.type}"/>
        <apex:inputField value="{!account.accountNumber}"/>
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

El atributo value nos permite indicar qué nombre le queremos poner al botón y el atributo action llamará al proceso que queremos ejecutar. Como seguimos trabajando con el Controlador Standard del objeto, simplemente debemos añadir “save” y Salesforce se encarga de realizar la acción de guardado por nosotros.

Captura de pantalla 2015-11-27 a las 19.13.21

Y después de modificar el valor de Account Number por 124 y darle al botón Save, accountview page muestra:

Captura de pantalla 2015-11-27 a las 19.16.51

¿Qué otras acciones nos permite Salesforce? En este link encontramos información sobre ellas, pero básicamente son:

  • Save
  • QuickSave
  • Edit
  • Delete
  • Cancel
  • List

 

Por ultimo, una ayuda para tener que evitar modificar el id de la cuenta cada vez que queremos mostrar un registro distinto. Si hemos creado estas páginas es porque queremos ofrecer esta vista al usuario final en vez de la standard. En ese caso, Salesforce nos permite sobre escribir estas vistas en el objeto.

Si vamos a:

Setup | Customize | Accounts | Buttons, Links and Actions

Modificaremos las vistas View y Edit

Captura de pantalla 2015-11-27 a las 19.20.23

En el caso de que queramos hacer lo mismo con un objeto creado por nostros, custom, en vez de Standard, solo tenemos que ir a

Setup | Create | Objects | <selecciona tu objecto> 

Y desde ahí baja a la sección Buttons,Links and Actions. La sobre escritura de la página se haría de forma similar a un objeto Standard.

En este post hemos aprendido:

  • Como crear una página editable
  • Diferencia entre inputField vs inputText y outputField vs outputText
  • Como añadir lista de un objeto relacionado a Cuenta
  • Como guarder modificaciones en la página de editado
  • Como evitar modificar la url con el id cada vez que quiero mostrar un registro

Y todo esto sin añadir una línea de código en Apex, solo Visualforce, ya que hemos usado el controlador Standard que los objetos nos ofrecen.

Data Pipeline and Salesforce – Do not break the rules

Captura de pantalla 2015-09-14 a las 16.19.20

Salesforce is working in a new feature called Data Pipeline that help us to integrate Apache Pig into Salesforce. Previous link can give you lot of information about what is Apache Pig but basically it is a Open Source technology that gives you mechanism for parallel processing of MapReduce jobs in a Hadoop cluster.

Here we can find 2 main keywords:

  • MapReduce: Software framework to write programs to execute large amount of unstructured data in parallel.
  • Hadoop Cluster: A special type of cluster that helps you to analyze and store large amount of unstructured data.

At the end we will say that:

Data Pipeline will help you to execute processes with large amount of data in parallel in Salesforce. 

Ok, I know what you are thinking right now. It sounds interesting but, I can get the same with other Salesforce features like @future, Queueable or Batch Apex. And that’s right. But the main thing here is that performance is much better and instead of having to wait maybe an hour in order to complete an asynchronous execution, Data Pipeline provides results in few minutes.

And how can we use it in Salesforce? We only need to create a single Pig Script using Pig Latin language (forget Apex) via Developer Console. And once we have it, click on Submit Data Pipeline button to execute the process.

Captura de pantalla 2015-09-11 a las 18.29.00

If you already know Apache Pig probably you know how to use Pig Latin and this pilot will be quite simple for your. But if this is not your case, I would like to start with 2 examples.

One of them will show you how to insert records and avoid getting an unexpected situation due to a validation.

The second one will help us to create records related by a Master – Detail relationship.

1st Use Case – How to create records avoiding breaking Salesforce rules

I have a new custom object called Header__c and I need to create some records taking Opportunity records as resource. The Pig script will look like this one. Something simple, where I retrieve some fields from Opportunity object and after doing a filter by Opportunity Name, store the result in the new Header custom object record.

Captura de pantalla 2015-09-10 a las 10.49.50

As you can see, the result is successful.

Captura de pantalla 2015-09-10 a las 10.50.03

And this is the new record in the list view

Captura de pantalla 2015-09-11 a las 17.38.16

But what does it happen if Header object has a validation that avoid any insertion if amount is not greater than 100? This is the validation rule:

Captura de pantalla 2015-09-10 a las 10.52.07

And if we run the same script, but using Opportunity2 as a source, this is what developer console returns.

Captura de pantalla 2015-09-11 a las 17.43.32

Ok, what is going on? The process finishes successfully. And what about the list view?

Captura de pantalla 2015-09-11 a las 17.38.16

Same as before. Just a single line. So how can we explain this?

We need to remember that Data Pipeline is going to execute records in batch, so if there is any failure during the script execution, and a record cannot be inserted, the process will continue with the rest, and finish successfully. We could compare it with Batch Apex, where the Apex Job screen shows us that an execution has finished properly without errors even if this means that some records where not inserted due to some validations that we have added in our code.

And how can we avoid this situation and not break the rules? Imagine this use case. This time we have 2 opportunities with the same name, but one of them has an amount less than 100.

Captura de pantalla 2015-09-11 a las 17.54.59

If I execute above script, just filtering by the new Opportunity Names, just one of them will be inserted

Captura de pantalla 2015-09-11 a las 18.05.29

But do we have a nicer and cleaner way to do it? We can also filter by amounts so we will avoid hitting the validation:

Captura de pantalla 2015-09-11 a las 18.22.44

Getting the below result

Captura de pantalla 2015-09-11 a las 18.26.18

2nd Use Case – Create a Master-Detail relationship

Another common situation is the use case where we need to create two records related by a Master-Detail relationship.

With above code examples we can think that this is not a big deal, but we have to keep in mind that this Pig Scripts doesn’t provide handle errors on the current release, and if for any reason the header is not created and we try to create lines related to them, the process can fail without our knowledge (as we show before).

So how can we solve this situation? Salesforce advice is to create 2 scripts, so after running the one that create Headers, we can run another that will create records of my new custom object called Line__c.

Captura de pantalla 2015-11-16 a las 19.34.36

What can we find on above script?

  • First of all we look for all Headers that we have in the Organization right now.
  • Look for all Opportunity Lines we have in the Organization as well.
  • Execute a Join by Opportunity Id, as both, Header__c and OpportunityLineItem have this field.
  • Finally I will sort the joined result, because we don’t need all fields for my new Line__c registers.
  • And Store, save, the information in the custom object.

And this is the result:

Captura de pantalla 2015-11-16 a las 19.41.25

Yes, so simple. Something to keep in mind? List fields on Store in the same order as we have defined in the variable that we will use for the population.

But looking at this code, we can think, why don’t do it in the same script? Remember that the process is asynchronous, so by the time we query Header__c records, maybe they are not inserted yet in the organization.

Anything to highlight?

  • Salesforce Data Pipeline is still in Pilot so in order to use it you need to ask Salesforce to give you access to it.
  • When will it be GA? My latest news, Summer ’16 but as always, Safe Harbor.
  • Cost? Price? Yes, it will have an extra cost apart from the licenses, but I don’t have the information to tell you how much.
  • Advantages?
    • Salesforce ensures that these executions would be faster than other asynchronous processes like Batch Apex or @Future.
    • Running Apache Pig into Salesforce help us to do this execution in a multi-tenant environment. However if we run a process in Apache Pig outside of Salesforce, executions are for a single user.
    • As we are not using Apex, we will not hit governor limits.
    • We will be able to add these scripts into packages so our customers can also run these processes.
  • Disadvantages?
    • We cannot create scripts with more than 20 operators, 20 loads and 10 stores.
    • Although we will not hit governor limits, we need to keep in mind that Store (Insert / Update) is done via Bulk API, so any restriction still apply.
    • Unfortunately we cannot call Pig Scripts from Apex. Only from Developer Console and from API 33.0 onwards, during deployment.
    • It doesn’t offer a debug tool, so if we are in the first use case and a record is not inserted, it would not be easy to find the reason quickly.
    • You cannot handle errors easily, it means, you cannot add something in your code like try / catch.

But this is not all. If you want to know more about Data Pipeline, you can also look at the presentation that Carolina Ruíz and myself run at Dreamforce ’15. Find the video Data Pipelines: Big Data Meets Salesforce.