dreamOlé ’17 – De la Comunidad para la Comunidad

Hace un año Carolina habló conmigo para organizar algo como London’s Calling pero en España. Al principio me pareció una idea un poco descabellada. Salesforce no tiene tanto tirón ahora mismo en nuestro país, pero precisamente por eso poco a poco se convirtió en una gran idea. Y aquí estamos Inés, Laura, Carlos, Julio, Yasmina, Roger, Alba, Carolina y yo quedando 2 veces por semana a las 8am para hablar de nuestro proyecto: dreamOlé.

Os puedo asegurar que nos costó mucho trabajo decidir los puntos principales:

El nombre – ¿Por qué dreamOlé?

Buscábamos algo que tuviera tirón, alguna canción como en Londres, pero “Aquí no playa vaya vaya” no nos parecía lo ideal. Así que pensamos en una combinación de algo típico español, feria, fiesta, sol, tapas, paella … pero sin ser topicazo. Y añadir la palabra dream cómo otros eventos similares. Y tras varias votaciones … nos quedamos con dreamOlé.

El lugar – OpenTalk – Madrid

Para nuestro primer evento, decidimos ir a lo seguro, Madrid ó Barcelona ya que hay una gran red de empresas relacionadas con Salesforce y podríamos tener público. Y finalmente nos decantamos por la capital de España. Y a partir de ahí a buscar el local. De verdad, no sabía que era tan complicado que incluso te cojan el teléfono … pero finalmente encontramos el lugar ideal para nuestro primer evento. Gracias OpenTalk !!

La fecha – 1 de Junio de 2017

Los primeros 6 meses del año son complicados. Hay muchos eventos de Salesforce por Europa, incluyendo el Essentials de Madrid y Barcelona. Así que buscamos un mes bonito en Madrid y rezamos para que no coincidiera con otras charlas, ya que algunos se anuncian con poca antelación.

¡Necesitamos un Logo!

Y echamos mano de nuestro compañero Jorge para este diseño. Fue verlo y enamorarme.

dreamOle-light@2x

 

La Agenda

No iba a ser un evento distinto a otros ya celebrados. Queremos acercar Salesforce a la comunidad, por eso nos centraremos en proporcionar sesiones donde compartir conocimientos. Estamos trabando para ofrecer charlas en inglés y español y sobre temas actuales, y por supuesto dirigidos a distintos públicos: Desarrolladores, Administradores, Usuarios finales …

Por otro lado, somos una organización sin ánimo de lucro organizando un evento que cuesta dinero, por lo que necesitamos la ayuda de patrocinadores para llevarlo a cabo. Para ello los sponsors Platinum y Gold tendrán un stand durante todo el día para promocionarse. Y además organizaremos un DemoJam dónde los sponsor Platinum tendrán 3 minutos para mostrar su producto ante todos los asistentes ya que no coincidirá con ninguna charla. Y al final habrá un ganador!!

Y por supuesto, tendremos un KeyNote inicial y final. Seguimos dando la lata a toda persona conocida en este mundillo para que venga y por ahora hemos convencido a Erica Kuhl, así que yo no me la perdería.

Y a partir de ahí seguimos trabajando para que todo esté listo a tiempo

¿Quieres saber más?  Síguenos en twitter ó Visita nuestra web. Y no te olvides. ¿Quieres ser sponsor? No te lo pienses porque tenemos plazas limitadas. Encuentra toda la información aquí.

¿Te gustaría dar una charla y compartir tus conocimientos? Mándanos tus ideas a través del Speaker Submission form y recuerda que la fecha límite es el 21 de Abril.

¿Tienes dudas? Visita nuestra sección FAQ y si algo no queda claro, ponte en contacto con nosotros a través de twitter ó el correo comunidadsalesforce@gmail.com

Nos vemos!!

Logo-O (1)

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.

 

 

 

Mi primera Visualforce IV – Controladores II

¡Cuánto tiempo! Pues si, en Enero fue mi último post en Español y 11  meses después vuelvo a escribir una entrada en nuestra lengua materna. Y es que últimamente he estado un pelín ocupada con mi bebe de 7 meses.

Esta será la última entrada con el título Mi primera Visualforce ya que terminaremos de cubrir conocimientos básicos. Pero no significa que no seguiré escribiendo en Español. Seguid atentos porque publicaré cosas.

¡Pero vamos al lío! Todos los objetos tienen asociados un controlador estándar que le proporciona funcionalidad (Échale un vistazo a esta entrada). Además, podemos crear controladores personalizados para extender y dar más potencia a nuestra página.

Además los controladores pueden tener un único elemento como argumento, como expliqué en mi última entrada, ó a una lista de registros.

Controlador asociado a varios registros

 

Controlador Estándar

Como recordarás, para usar el controlador estándar, sólo tienes que usar el atributo standardController del tag apex:page

<apex:page standardController="Account">
</apex:page>

Entonces, ¿cuál es la diferencia? Como supondrás, necesitaré una variable para poder almacenar todas las cuentas, y después iterar sobre ella. Para ello, apex:page ofrece otro atributo recordsetVar cuyo valor será la lista de registros. Una vez que la tengamos, sólo tenemos que mostrar los campos que deseemos de nuestra cuenta. En el código de más abajo, usaremos el tag apex:repeat aunque podríamos haber usado también apex:datatable como en posts anteriores.

<apex:page standardController="Account" recordsetVar="records">
   <apex:form >
      <apex:pageBlock title="Listado de Cuentas">
         <apex:pageBlockSection columns="1">
            <apex:repeat value="{!records}" var="acc">
               <apex:outputField value="{!acc.Name}"/>
            </apex:repeat>
         </apex:pageBlockSection>
      </apex:pageBlock>
   </apex:form>
</apex:page>

Siendo este el resultado

Captura de pantalla 2016-11-07 a las 18.52.19.png

Controlador Personalizado

¿Y cómo puedo conseguir lo mismo pero con un controlador creador por mi mismo? Sólo tienes que seguir estos pasos:

Crear un Controlador en Apex

Una clase en Apex sencilla. Pero ten en cuenta lo que explicamos en el último post sobre nomenclatura, sharings, etc. Lo único que debemos tener en cuenta es el constructor. Esta vez, en vez de utilizar ApexPages.StandardController, usaremos la clase ApexPages.StandardSetController que nos permitirá recuperar la lista de registros seleccionados (por ejemplo) con el método getRecords de forma similar que el controlador de único registro nos ofrecía el método getRecord. Pero además, en este caso, tenemos métodos para facilitar la paginación cómo getHasNext ó getHasPrevious.

public with sharing class AccountListController
{
   public AccountListController(ApexPages.StandardSetController controller)
   {
      //TODO añadir código
   }
}

Añadir método para recuperar la lista de cuentas

Muy sencillo. Añadimos la variable registroCuentas con sus métodos get y set. Por otro lado, añadimos un método que recuperará las listas que tenemos en base de datos por medio de una SOQL calcularCuentas. Y por último, en el constructor, rellenamos la variable privada con la información.

public with sharing class AccountListController
{
   private List<Account> registroCuentas;
 
   public AccountListController(ApexPages.StandardSetController controller)
   {
      setRegistroCuentas(calcularCuentas());
   }
 
   public List<Account> calcularCuentas()
   {
      try
      {
         return [Select Id, Name From Account];
      }
      catch(Exception ex)
      {
         ApexPages.addMessages(ex);
         return null;
      }
   }
 
   public List<Account> getRegistroCuentas()
   {
      return registroCuentas;
   }
 
   public void setRegistroCuentas(List<Account> value)
   {
      registroCuentas = value;
   }
}

Modificar la página para usar el Controlador

En este último paso sólo tenemos que añadir el atributo extensions cuyo valor es el nombre del controlador personalizado que hemos creado. Además, debemos usar nuestro método como valor en el atributo recordSetVar.

<apex:page standardController="Account" 
           extensions="AccountListController" 
           recordsetVar="registroCuentas">
   <apex:form >
      <apex:pageBlock title="Listado de Cuentas Personalizado">
         <apex:pageBlockSection columns="1">
            <apex:repeat value="{!registroCuentas}" var="acc">
               <apex:outputField value="{!acc.Name}"/>
            </apex:repeat>
         </apex:pageBlockSection>
      </apex:pageBlock>
   </apex:form>
</apex:page>

captura-de-pantalla-2016-11-07-a-las-19-23-30

Como puedes ver, el look & feel de la página es igual que la página que usa el controlador estándar.

A partir de ahí, eres tu quien pone los límites en una página.

Alternativa

Por último, si nuestro caso de uso es simplemente mostrar la lista de Cuentas, pero no nos piden nada más (por ejemplo mostrar sólo unos campos), podemos usar el tag apex:enhancedList y no reinventar la rueda. Con esta línea de código, podemos mostrar todas las cuentas de nuestra organización junto con sus enlaces de editar, borrar, seleccionar, etc y sin ningún esfuerzo.

<apex:page >
   <apex:enhancedList type="Account" 
                      height="300" 
                      rowsPerPage="10" 
                      id="AccountList" />
</apex:page>

 

captura-de-pantalla-2016-11-08-a-las-17-45-17

Esta ha sido una entrada sencilla en la que hemos aprendido

  • Mostrar una lista de Cuentas por medio del controlador Standard
  • Mostrar una lista de Cuentas via un controlador personalizado
  • Mostrar una lista de Cuentas usando el tag apex:enhancedList 

Nos vemos en el próximo post.

Dreamforce ’16 – What I did not tweet

One of the first things I did after coming back from my maternity leave, was to attend Dreamforce ’16. And this post is about this amazing event. However I would like to write something different from others. First of all, don’t expect a technical entry where I praise all Salesforce news. They were really good this year, but DF is more than a technical event. It’s networking, it’s enjoy the place, the sun. It’s comeback with ideas to explore at home and keep feelings of these sort time.

So, what are you going to read here? My experience but with small sentences. Although my smartphone was ready to write tweets, I did not have time for it. It was write or listen, so I decide to pay attention and make pics.

So, What I did not tweet?

Day 1 – 4th of October

06:00 – Start my #DF16 trip. @Bilbao airport. Long queue for checking. Will I miss my flight? It’s a bit delayed … so far so good.

09:30 – At Schiphol airport. Close to lose my connection to San Francisco!! So lets do some exercise, run along Amsterdam airport!! I get it. Sit down and #relax for the next 11 hours.

11:45 – #Border has no end 😦  But I’m on time for #FinancialForce DevTalks. Starting to feel tired.

13:00 – Where is my luggage? This suitcase is similar to mine but it is not mine!! Sorry Mrs García, not sure where your belongings are 😦 😦 😦 #luggagelost #noclotheswithme

14:00 – Arrive to the hotel with a new San Francisco T-shirt bought at the airport and looking forward to having a shower. Sorry Mrs García, your room is not available yet. You would have to wait. What?? 😦 😦 😦 😦 😦 😦 #anythingelse?

img_4929

 

 

14:30 – Dreamforce registration. Here is my credential!!

 

 

 

 

 

17:00 (2:00 in Spain) – FinancialForce Dev Talks’16 starts!! Nice to share the stage with Andy Fawcett and Jesse Atlman

img_1256

19:00 – Time for #MVP dinner … I cannot do it. Tired and need to buy some clothes for tomorrow. But I think that I missed my #amazingbbq 🙂

Day 2 – 5th of October

img_4808

 

7:00 – Nice views from my room #lovesunnydays

 

11:00 – @Salesforce Circle of Success session. Success: arrive on time – lost in the hotel. No session success … 1 attendee in a table with 5 MVPs. Poor him lot of questions about what he needed. Should it be in the other way around? @Salesforce, #2far2lazy2go.

 

 

12:30 – Searching my #MVP seat @Salesforce Keynote. I’m so exciting!! This year I’m not on the last row of the room (literally last one).

13:00 – @Salesforce Keynote starts !! Don’t miss it.

13:15 – @Will.I.am and WHERESTHELOVE #ovation

img_4807

14:00 – Einstein makes your app smarter. On my #todo list for home.

14:30 – @Red moment. It’s not charity, it’s justice. Where you have born shouldn’t determine how long is your life.

14:45 – Never been so close to Mark Benioff #FanMoment 😀

 

 

 

17:00 – Moscone West has changed a lot!! New location for #Trailhead area and Theaters #newworld #admin #developers

img_4853

20:30 – Salesforce Gala starts with U2 “Uno, dos, tres, catorce!!”

img_4819

Day 3 – 6th of October

07:30 – Starting the day with energy @Mel’sDinner and my @FinancialForce colleagues

img_4832

08:30 to 17:00 – @SalesforceDevelopers day. #Heroku #Gamification #RESTAPI #SOAPAPI #NewPlatformEventObject #BigObjects and #Developer KeyNote with Jesse and David Frudd

img_4848

 

20:00 – A tired day ends with #clamchowder

 

 

 

 

Day 4 – 7th of October

9:00 – Time to come back @home but before taking, pic of Moscone area and Salesforce arch. Looking forward to coming back again. #DF17 the 6th of November.

Platform Developer Certification – Transition Exam

As a developer, I wanted to learn as much as possible and a good way to do it is getting any of the certifications that Salesforce offers to us.

I was able to get 401 and 501 credentials, so I decided to share some tips about them writing an entry some months ago.

However a year ago, Salesforce decided to modify certifications and 401 and 501 were not available anymore.

So what could I do? Do I lose my credentials? No at all. I still can say that I got these certifications. However Salesforce also advices to us to move to the new one.

In order to do it we have different paths as you can see on below image:

Captura de pantalla 2016-07-12 a las 15.09.09

I decided to go for the combo Transition Exam, but lot of questions came to my mind. So let me share all answers I got. Maybe they can also help you.

Q – How is the Exam?

A – This is a test exam. We have 30 minutes to answer 16 multi-choice questions, and we need to get 63% (11 questions) right to pass the exam. Per my experience, don’t waste the time. Read carefully and if you are not completely sure, mark to review it later and move to the next one.

Q – When do I get the results?

A – After submitting the exam. As usual, we would get the Pass or Fail response. Unfortunately we don’t get the score. You can only see if you passed it or not.

Q – Is this a proctored exam?

A – Yes !! So take care and prepare your place for the exam in advance.

Q – What can I do if I fail the exam?

A – Good questions!! That also worried to me. This is a free exam and you can try to pass it as many times as you need. The only thing that you need to take into account is that you cannot try it more than 3 times per release. This mean. Salesforce deliver 3 releases a year, so you have 9 opportunities to do it per year.

Q – What should I study to pass it? 

A – There is no guide to know what you need. To be honest, you should get Platform Developer I and II guides and check what it is required for them and study the same. I reviewed all the material that I used to pass 501 exam. And in addition, I checked:

Obviously, Trailheads are also very useful to remember certain contents for example Life cycle one.

Q – Should I look for any other material?

A – Something I didn’t find on above documents and I was asked for was about:

  • Compounds fields
  • Certifications, kind of certifications depending on device …
  • Streaming API

Actually the exam was a challenged and that also helped me to learn even more.

Q – What kind of questions could I find?

A – This test exam provided different options and you need to select one or many responses.

An example could be (This is not a question of my exam. This is an example I have created based on what I found during the exam)

Having this piece of code:

@RestResource(urlMapping='/user_defined_type_example/*')
global with sharing class MyOwnTypeRestResource 
{
   @HttpPost
   global static MyUserDefinedClass echoMyType(MyUserDefinedClass ic)
   {
      return ic;
   }

   global class MyUserDefinedClass
   {
      global String string1;
      global String string2 { get; set; }
      private String privateString;
      global transient String transientString;
      global String staticString;
   }
}

And these JSON calls:

1. 
{
   "ic" : {
             "string1" : "value for string1",
             "string2" : "value for string2",
             "privateString" : "value for privateString"
          }
}

2.
{
   "ic" : {
             "string1" : "value for string1",
             "string2" : "value for string2"
          }
}

3.
{
   "ic" : {
             "string1" : "value for string1",
             "string2" : "value for string2",
             "transientString" : "value for transientString",
             "staticString" : "value for staticString"
           }
}

Which is the correct call?
a) 1
b) 2
c) 3
d) 1 and 3
e) 1 and 2
f) 1, 2 and 3

Q – Once I pass the exam. Do I have to do anything else? Assignment or any other test?

A – No !! Since that moment you get your new credentials and you only need to do the maintenance exam as usual 3 times a year.

Q – I have Force.com Developer (401) certification. Does the maintenance exam cover also that one?

A – I’m afraid that Platform Developers maintenance don’t cover 401 certifications, so you would have to do 2 exams per release. But nothing to worry too much.

Do you have any other question? You can open a case here.

Once you get it you can also check your certification in the verification page. Something that I will miss from this page is my Advance Developer logo.

Captura de pantalla 2016-07-12 a las 15.03.17

By the way, the correct answer on above question is option e).

Mi primera Visualforce III – Controladores

En este post vamos a continuar desde dónde lo dejamos. En la última entrada en Español hablábamos de cómo crear una página visualforce utilizando únicamente un Controlador Standard. Si todavía no has tenido la oportunidad de leerla sería interesante que lo hicieras antes de continuar. Aquí tienes el link.

Controlador Personalizado

Hasta ahora sólo hemos usado el controlador Standard asociado al objeto que usábamos en la página, Account. Y con él hemos conseguido hacer muchas cosas. Entonces, ¿por qué necesito crear uno? Con este tipo de controladores vamos a extender la funcionalidad, de forma que nuestra página puede llegar a ser más potente si cabe.

Tenemos dos tipos de controladores personalizados:

  • Asociados a una lista de registros de un objeto
  • Asociados a un único registro de un objeto

Dejaremos la lista de registros para un blog futuro y hoy vamos a centrarnos en qué hacer con un único registro.

Único Registro

Estos controladores se usarán cuando quiero realizar una acción asociada a un registro en concreto.

Para empezar sólo tengo que crear una clase Apex

Setup | Develop | Apex Classes

Y hacer click en el botón New.

A continuación sólo tienes que escribir estas líneas de código y guardar:

public with sharing class AccountController
{
    public AccountController(ApexPages.StandardController controller){}
}

Antes de continuar ¿qué podemos destacar del código anterior?

  1. Una clase puede ser:
    1. private: la clase es sólo conocida de forma local, es decir por el código de esa clase.
    2. public: la clase es visible en toda la aplicación.
    3. global: la clase es visible por cualquier código Apex en cualquier sitio, es decir, si empaquetamos la clase, ésta será visible y accesible para cualquier usuario que haya instalado el paquete. Mi recomendación por ahora es que tu clase sea pública de forma que así también puedes proteger la propiedad intelectual de tu clase si decides comercializarla en un futuro.
  2. With Sharing nos permite indicar permisos de acceso. Échale un vistazo a este blog en inglés de Andy Fawcett dónde explica el significado de estás palabras
  3. Palabra Controller en el nombre de la clase. Es una opción personal para saber que esa clase es un Controlador, pero si tu prefieres darle otro nombre, ¡adelante! sólo una recomendación. Mantén una misma convención en cuanto a nombres se refiere a lo largo de todo tu desarrollo, así que si creas otro controlador, dale un nombre similar
  4. Un constructor con un único argumento del tipo ApexPages.StandardController que proporciona una serie de métodos para poder recoger información del registro asociado a ese controlador

Una vez que tenemos el controller creado, vamos a asociarlo a una página. Por ejemplo, vamos a extender la funcionalidad de la página accountedit que creamos en el post anterior. Para ello sólo tenemos que añadir el atributo extensions a la definición de la página.

<apex:page standardController="Account" extensions="AccountController">
</apex:page> 

Y ahora, ¿tenemos que hacer algún otro cambio? No en principio podemos mantener el mismo código en la página aunque lo asociemos a un controlador custom.


<!-- accountedit -->
<apex:page standardController="Account" extensions="AccountController">
  <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>

Como ya aprendimos en el post anterior, si sobre escribimos el botón Edit del objeto Cuenta para mostrar nuestra página, el resultado será el siguiente:

Captura de pantalla 2016-01-18 a las 16.31.57

Entonces, ¿cual es la diferencia?

Por ahora ninguna.

Pero vamos a seguir añadiendo funcionalidad. En la página view del anterior post, mostrábamos además una lista relacionada con las Oportunidades asociadas a la cuenta seleccionada. Vamos a hacer lo mismo en nuestra página pero esta vez vamos a usar el tag  apex:BlockTable.  Este tag tiene como atributo value que usaremos para llamar al método que nos devuelve la lista de registros sobre la que vamos a iterar en la tabla. Y el atributo var que será la variable que usaremos en cada iteración.

<apex:pageBlockTable title="Opportunities" value="{!opportunityList}" var="opp">
</apex:pageBlockTable>

¿Y qué es opportunityList? Ahí es cuando entra en acción nuestro nuevo controlador dónde tendremos un método que via una consulta a base de datos (en Salesforce se llaman SOQL) obtendremos la lista de Oportunidades asociadas a la cuenta Account1.

Una vez que tenga esta información, usaremos los métodos get / set que al final serán los que el atributo value llamará.


public with sharing class AccountController
{
    private Id m_accountId;
    private List<Opportunity> m_opportunities;
    private ApexPages.StandardController m_controller;

    public AccountController(ApexPages.StandardController controller)
    {
        m_controller = controller;
        m_accountId = m_controller.getRecord().Id;
        m_opportunities = calculateOpportunities(m_accountId);
    }
   
    public List<Opportunity> getOpportunityList()
    {
        return m_opportunities;
    }

    public void setOpportunityList(List<Opportunity> value)
    {
        m_opportunities = value;
    }

    private List<Opportunity> calculateOpportunities(Id accId)
    {
        //Remember Separation of Concerns
        //Move to a Selector class
        List<Opportunity> oppList = [Select Id, Name From Opportunity Where AccountId = :accId];

        return oppList;
    }
}

Cómo hemos mencionado antes del código, tenemos una variable privada m_opportunities que será utilizada por el get / set. Y si te fijas, estos métodos tienen el nombre que hemos usado en nuestra página visualforce.

Además, tenemos un método calculateOpportunities donde vamos a hacer la consulta a base de datos, la SOQL. Recuerda que aunque en este ejemplo estoy haciendo una SOQL en un controlador, debemos mantener el SOC. Para más información echa un vistazo aquí.

Y este método será llamado en el constructor del controlador y lo usaremos para modificar la variable privada m_opportunities de forma que una vez que se llame al controlador, la lista de Oportunidades se rellenará automáticamente.

Otra cosa a destacar, es el filtro de nuestra consulta. Buscamos las Oportunidades cuyo campo Account tenga el mismo valor que la cuenta que tengo en mi página. Si recuerdas, ya hemos comentado que StandardController ofrece muchos métodos, entre ellos getRecord() que nos devolverá el registro que estamos visualizando en el momento de llamar a nuestro controlador.

El último paso antes de visualizar el resultado, es añadir alguna columna a nuestra tabla. Por ahora sólo añadiremos el Nombre de la oportunidad.


<apex:pageBlock title="Related Lists">
  <apex:pageBlockTable title="Opportunities" value="{!opportunityList}" var="opp" >
    <apex:column headerValue="Name">
      <apex:outputText value="{!opp.Name}"/>
    </apex:column>
  </apex:pageBlockTable>
</apex:pageBlock>

Sabiendo todo esto, ¿cual es el resultado?

Captura de pantalla 2016-01-21 a las 19.51.57.png

¿Pero y si queremos añadir un campo que no está en el objeto Oportunidad y mostrarlo en la tabla? Por ejemplo, me gustaría visualizar un checkbox que me indicara si la Oportunidad tiene estado Closed Won.

Al ser un campo calculado, la lista que devolveremos no será directamente del tipo Oportunidad. Lo que vamos a hacer es crearnos una clase interna con dos variables, por un lado el registro de la oportunidad, y por otro este nuevo campo que vamos a calcular. Una vez que lo tengamos, la lista que usaremos en la página será del tipo de la clase interna.

Por otro lado, en el método que calcula las Oportunidades asociadas a la Cuenta, haremos los cálculos pertinentes para rellenar el nuevo campo que crearemos.

Así quedaría el controlador:


public with sharing class AccountController
{
    private Id m_accountId;
    private List<OpportunityInfo> m_opportunities;
    private ApexPages.StandardController m_controller;

    public AccountController(ApexPages.StandardController controller)
    {
        m_controller = controller;
        m_accountId = controller.getRecord().Id;
        m_opportunities = calculateOpportunities(m_accountId);
    }

    public List<OpportunityInfo> getOpportunityList()
    {
        return m_opportunities;
    }

    public void setOpportunityList(List<OpportunityInfo> value)
    {
        m_opportunities = value;
    }

    private List<OpportunityInfo> calculateOpportunities(Id accId)
    {
        Boolean isClosed;
        OpportunityInfo oppInfo;
        List<OpportunityInfo> oppInfoList = new List<OpportunityInfo>();

        //Remember Separation of Concerns
        //Move to a Selector class
        for(Opportunity opp : [Select Id, Name,
                                 StageName, Amount,
                                 CloseDate
                               From Opportunity
                               Where AccountId = :accId])
        {
            isClosed = opp.StageName == 'Closed Won' ? true : false;
            oppInfo = new OpportunityInfo(opp, isClosed);
            oppInfoList.add(oppInfo);
        }
        return oppInfoList;
     }

     //Inner class
     private class OpportunityInfo
     {
        public Opportunity OpportunityRecord {get; private set;}
        public Boolean ToClose {get; set;}

        private OpportunityInfo(Opportunity oppValue, Boolean toCloseValue)
        {
            OpportunityRecord = oppValue;
            ToClose = toCloseValue;
        }
      }
}

Y en la página visualforce, la tabla tendrá que hacer uso de la clase interna


<apex:pageBlock title="Related Lists">
   <apex:pageBlockTable title="Opportunities" value="{!opportunityList}" var="opp" >
      <apex:column headerValue="Name">
         <apex:outputField value="{!opp.OpportunityRecord.Name}"/>
      </apex:column>
      <apex:column headerValue="is Closed?">
         <apex:inputCheckbox value="{!opp.ToClose}" disabled="{!opp.ToClose}"/>
      </apex:column>
      <apex:column headerValue="Stage">
         <apex:outputField value="{!opp.OpportunityRecord.StageName}"/>
      </apex:column>
      <apex:column headerValue="Amount">
         <apex:outputField value="{!opp.OpportunityRecord.Amount}"/>
      </apex:column>
      <apex:column headerValue="CloseDate">
         <apex:outputField value="{!opp.OpportunityRecord.CloseDate}"/>
      </apex:column>
   </apex:pageBlockTable>
</apex:pageBlock>

Siendo el resultado:

Captura de pantalla 2016-01-21 a las 20.11.08.png

En este ejemplo todos los campos de la tabla son de sólo lectura. El único que es input es el campo personalizado que hemos creado a partir de la clase interna. Pero si te fijas, en todos los registros el checkbox es de tipo lectura menos en el último en que el campo es de escritura porque el Stage no es Closed Won. Sin embargo el tag que hemos usado en todos los casos es apex:inputCheckbox. ¿Por qué no usamos output? Porque este tag sólo da la opción de escritura. Sin embargo ofrece un atributo para deshabilitarlo y es el que hemos usado, disabled.

Por último, nos podríamos preguntar por el botón Save. Si mantenemos en nuestra página visualforce la misma línea para el botón save que en el post anterior, la funcionalidad será la misma. Usará el controlador Standard para hacer la modificación en base de datos.

Sin embargo podemos modificar esa funcionalidad para extenderla. Para ello tendremos que crear un método en nuestro controlador dónde añadiremos la funcionalidad que necesitemos y devolveremos la página hacia donde quiero navegar después de la modificación. En nuestro caso la versión sólo lectura de nuestra cuenta.

public PageReference saveUpdates()
{
    //TODO: Acciones para salvar
    return new PageReference('/' + m_accountId);
} 

Y hacer una llamada a ese método en nuestra página.

<apex:pageBlockButtons >
    <apex:commandButton action="{!saveUpdates}" value="Save"/>
</apex:pageBlockButtons> 

Como resumen, en esta entrada hemos aprendido:

  • Cómo crear un controlador asociado a un único registro
  • Cómo asociarlo a una visualforce
  • Cómo mostrar campos en nuestra página visualforce
  • Cómo crear un tabla con valores que vienen de base de datos
  • Cómo añadir a la tabla un campo que no existe en base de datos
  • Cómo modificar el botón Standard Save

Nos vemos en el próximo post.

 

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.