Empiece a realizar un seguimiento de su progreso
Inicio de Trailhead
Inicio de Trailhead

Descubrir las acciones de Lightning

Objetivos de aprendizaje

Después de completar esta unidad, podrá:
  • Crear un componente y una acción Aura para completar campos en función de la entrada del usuario y generar mensajes de comentarios para los usuarios.
  • Crear un componente y una acción Aura que se integren con un sistema externo.
  • Iniciar la migración de la funcionalidad de los botones personalizados de JavaScript a alternativas compatibles con Lightning.

Acciones de Lightning: inteligentes, rápidas y móviles

Hemos descrito varias soluciones que funcionan tanto en Lightning Experience como en Salesforce Classic además de ser un excelente reemplazo de los botones de JavaScript. No obstante, hemos de admitir que estas soluciones declarativas y programáticas no se adaptan a todos los casos de uso. La ventaja es que contamos con una opción que nos permite abarcar mucho más. Presentamos las acciones de Lightning, un tipo de acciones rápidas que llaman a componentes Lightning.
Nota

Nota

Desde la versión Spring ‘19 (versión 45.0 de la API), puede construir componentes Lightning empleando dos modelos de programación: el modelo Componentes web Lightning y el modelo Componentes Aura original. Los componentes web Lightning y los componentes Aura pueden coexistir y funcionar conjuntamente en una página. Este contenido trata los componentes Aura.

Las acciones Lightning están construidas sobre el marco de trabajo de componentes Lightning existente. Puede convertir fácilmente los componentes Aura existentes en acciones y usarlas en la aplicación móvil Salesforce y Lightning Experience.

Para poder invocar un componente Aura como una acción, agregue una de estas dos interfaces al componente: force:lightningQuickAction o force:lightningQuickActionWithoutHeader. La primera interfaz agrega el encabezado estándar con los botones Guardar y Cancelar a la superposición de la acción de Lightning, pero la otra interfaz no sirve para esto.

Otra interfaz útil para una acción de Lightning es force:hasRecordId, la cual proporciona el contexto de registro para el componente cuando se invoca desde una página de registro. Si configura su acción de Lightning como una acción rápida global, no necesita el contexto de registro. No obstante, si desea acceder a los datos o metadatos de un registro, debe implementar force:hasRecordId.

Nota

Nota

Si no ha creado componentes Aura antes, visite el centro de desarrollo de Lightning, complete el proyecto de inicio rápido de componentes Aura y complete el módulo Fundamentos de Componentes Aura.

Profundicemos en las acciones de Lightning. A continuación, describiremos parte de la funcionalidad de los botones de JavaScript que se puede aplicar con acciones de Lightning.
  • Llenado de campos en función de la entrada del usuario y generación de mensajes de comentarios durante la entrada de datos
  • Integración con API externas

Completar campos en función de la entrada del usuario y generar mensajes de comentarios para los usuarios

Supongamos que usa botones de JavaScript para validar o manipular datos y proporcionar a los usuarios comentarios o instrucciones para su trabajo con los registros. Este es un ejemplo en el que se demuestra la facilidad con la que puede validar o manipular los datos en un componente Lightning antes de que los usuarios creen o actualicen un registro.

Hemos creado un objeto personalizado en nuestra organización de ejemplo, llamada Caso práctico, el cual usamos para hacer un seguimiento de distintos proyectos de investigación. Al agregar nuevos usuarios de prueba, capturamos sus nombres y direcciones de email. Dado que la dirección de email es nuestro método de contacto principal, queremos asegurarnos de que se ingresa correctamente. Además, queremos crear un apodo único para los usuarios de prueba de modo que puedan seguir siendo anónimos.

Vamos a crear una acción de Lightning que muestre los campos de nombre y dirección de email, valide la dirección de email y use el nombre y un número aleatorio para crear el apodo automáticamente.

Este es el código para el componente al que llama esta acción de Lightning.
  • CreateUser.cmp: El componente Lightning que ve al abrir la acción. Contiene la implementación de la interfaz de usuario, lo que incluye campos de texto, botones, el título de la acción, etc.
  • CreateUserController.js: El controlador que escucha el componente y cualquier evento de interfaz de usuario, como la carga inicial, la entrada de texto y los clics en botones. Su función es delegar estos eventos en la clase auxiliar CreateUserHelper.js.
  • CreateUserHelper.js: El código que trata toda la lógica de negocio, como la validación de los campos de email y contraseña.

CreateUser.cmp

<aura:component implements="force:lightningQuickActionWithoutHeader,force:hasRecordId">
    <aura:attribute name="user" type="Test_User__c" default="{}"/>
    <aura:attribute name="hasErrors" type="Boolean" description="Indicate whether there were failures or not" />
    <aura:attribute name="caseStudy" type="String" />
    <aura:attribute name="recordId" type="String"/>
    <aura:attribute name="errorFromCreate" type="String"/>
    <!-- <aura:handler name="init" value="{!this}" action="{!c.init}" />  -->
    <force:recordData aura:id="frd" mode="EDIT" layoutType="FULL"/>
    <div class="slds-page-header" role="banner">
    	<p class="slds-text-heading--label">Case Study</p>
        <h1 class="slds-page-header__title slds-m-right--small slds-truncate slds-align-left" title="Case Study Title">{!v.caseStudy}</h1>
    </div>
    <br/>
    <aura:if isTrue="{!v.hasErrors}">
        <div class="userCreateError">
            <ui:message title="Error" severity="error" closable="true">
                Please review the error messages.
            </ui:message>
        </div>
    </aura:if>
    <div class="slds-form--stacked">
        <div class="slds-form-element">
            <label class="slds-form-element__label" for="firstName">Enter first name: </label>
            <div class="slds-form-element__control">
              <ui:inputText class="slds-input" aura:id="firstName" value="{!v.user.First}" required="true" keydown="{!c.updateNickname}" updateOn="keydown"/>
            </div>
        </div>
        <div class="slds-form-element">
            <label class="slds-form-element__label" for="lastName">Enter last name: </label>
            <div class="slds-form-element__control">
              <ui:inputText class="slds-input" aura:id="lastName" value="{!v.user.Last}" required="true" />
            </div>
        </div>
        <div class="slds-form-element">
            <label class="slds-form-element__label" for="nickname">Enter nickname: </label>
            <div class="slds-form-element__control">
              <ui:inputText class="slds-input" aura:id="nickname" value="{!v.user.Nickname}" required="false"/>
            </div>
        </div>
        <div class="slds-form-element">
        	<label class="slds-form-element__label" for="userEmail">Enter user's email:</label>
            <div class="slds-form-element__control">
        		<ui:inputEmail class="slds-input" aura:id="userEmail" value="{!v.user.Email__c}" required="true"/>
            </div>
        </div>
        <div class="slds-form-element">
        	<label class="slds-form-element__label" for="userPassword">Enter user's password:</label>
            <div class="slds-form-element__control">
        		<ui:inputSecret class="slds-input" aura:id="userPassword" value="{!v.user.Password__c}" required="true"/>
            </div>
        </div>
        <div class="slds-form-element">
        	<ui:button class="slds-button slds-button--neutral" press="{!c.cancel}" label="Cancel" />
        	<ui:button class="slds-button slds-button--brand" press="{!c.saveUserForm}" label="Save User" />
        </div>
    </div>
</aura:component>

CreateUserController.js

({
    /**
     * Auto generate the username from firstName on tab
     */
    updateNickname: function(component, event, helper) {
        // Update the nickname field when 'tab' is pressed
        if (event.getParams().keyCode == 9) {
        	var nameInput = component.find("firstName");
        	var nameValue = nameInput.get("v.value");
        	var nickName = component.find("nickname");
            var today = new Date();
        	nickName.set("v.value", nameValue + today.valueOf(today));
        }
    },
    /**
     * Capture the Inputs and invoke the helper.save with the input params
     */
	saveUserForm : function(component, event, helper) {
        var name = component.get("v.user.First");
        var last = component.get("v.user.Last");
        var password = component.get("v.user.Password__c");
        var email = component.get("v.user.Email__c");
        var nickname = component.get("v.user.Nickname");
        var passwordCmp = component.find("userPassword");
        var emailCmp = component.find("userEmail");
        helper.validatePassword(component, event, helper);
        helper.validateEmail(component, event, helper);
        if (passwordCmp.get("v.errors") == null && emailCmp.get("v.errors") == null) {
            component.set("v.hasErrors", false);
        	helper.save(component, name + " " + last, password, email, nickname);
        } else {
            component.set("v.hasErrors", true);
        }
    },
    cancel : function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    }
})

CreateUserHelper.js

({
    save: function(component, name, password, email, nickname) {
        // Create a user record, save it, and close the panel
        var userRecord = {apiName: 'Test_User__c', fields: {}};
        userRecord.fields.Name = {value: name};
        userRecord.fields.Password__c = {value: password};
        userRecord.fields.Email__c = {value: email};
        userRecord.fields.Nickname__c = {value: nickname};
        userRecord.fields.Case_Study__c = {value: component.get("v.recordId")};
        // get the force:recordData and set the targetRecord
        component.find("frd").set('v.targetRecord', userRecord);
        // invoke saveRecord of force:recordData
        component.find("frd").saveRecord($A.getCallback(function(response) {
            if (component.isValid() && response.state == "SUCCESS") {
                $A.get("e.force:closeQuickAction").fire();
                var toastEvent = $A.get("e.force:showToast");
                toastEvent.setParams({
                	"title": "Success!",
                    "message": "The test user has been created."
                });
                toastEvent.fire();
                $A.get('e.force:refreshView').fire();
            } else if (response.state == "ERROR") {
                console.log('There was a problem and the state is: '+ response.state);
            }
        }));
    },
    validatePassword : function(component, event, helper) {
        var inputCmp = component.find("userPassword");
        var value = inputCmp.get("v.value");
        if (value == undefined) {
           inputCmp.set("v.errors", [{message: "You must enter a password."}]);
        } else if (value.length < 7 || value.length > 15) {
            inputCmp.set("v.errors", [{message: "The password is the wrong length (must be <= 15): " + value.length}]);
        } else if (value.search(/[0-9]+/) == -1) {
            inputCmp.set("v.errors", [{message: "The password must contain at least one number."}]);
        } else if (value.search(/[a-zA-Z]+/) == -1) {
            inputCmp.set("v.errors", [{message: "The password must contain at least one letter."}]);
        } else {
            inputCmp.set("v.errors", null);
        }
	},
    validateEmail : function(component, event, helper) {
        var inputCmp = component.find("userEmail");
        var value = inputCmp.get("v.value");
        if (value == undefined) {
           inputCmp.set("v.errors", [{message: "You must enter an email."}]);
           return;
        }
        var apos = value.indexOf("@");
        var dotpos = value.lastIndexOf(".");
        if (apos<1||dotpos-apos<2){
            inputCmp.set("v.errors", [{message: "Email is not in the correct format: " + value}]);
        } else if (value.substring(apos+1, dotpos) != "gmail") {
            inputCmp.set("v.errors", [{message: "Email must be a gmail account: " + value.substring(apos+1, dotpos)}]);
        } else {
            inputCmp.set("v.errors", null);
        }
	}
})

Después de crear el componente Lightning, lo asignamos a una acción. En la configuración de gestión de objetos para el caso práctico, vamos a Botones, vínculos y acciones, hacemos clic en Nueva acción y, a continuación, configuramos la acción con estos parámetros.

Campo Valor
Nombre de objeto Caso práctico
Tipo de acción Componente Lightning
Componente Lightning c:CreateUser
Altura 500px
Etiqueta Crear usuario de prueba
Nombre CreateUser

A continuación, agregamos la nueva acción de Lightning al formato de página del caso práctico. Cuando los usuarios la invocan desde una página de registro del caso práctico, ven la acción de Lightning que hemos creado.

Superposición de la acción de Lightning para la creación del usuario de prueba

Lo mejor de esta acción de Lightning es que también funciona en la aplicación móvil Salesforce.

Acción Estudio de caso en la aplicación Salesforce

Integrar API externas

Es posible que use botones de JavaScript para la integración con sistemas externos. ¿Puede usar las acciones de Lightning para esto? Por supuesto. La diferencia principal es que, para la integración mediante acciones de Lightning, debe usar un controlador de servidor. Sin embargo, esto le permite controlar mejor las credenciales de seguridad y le ofrece la posibilidad de usar Apex para llamadas a API asíncronas y por lotes.

Vamos a describir cómo realizar una integración con Twilio para el envío de mensajes SMS. En este ejemplo, nos vamos a centrar en el negocio de los viajes de lujo y nos relacionaremos con celebridades y personas muy importantes. Nuestro objetivo es que los agentes del servicio de atención al cliente puedan comunicarse con sus clientes, pero no queremos revelar en ningún caso la información de contacto personal de los clientes. Creamos una acción de Lightning en el objeto Contacto para que los agentes puedan enviar mensajes sin ver el número de teléfono del contacto.

Como en nuestro ejemplo de caso práctico, creamos el componente Aura y clases auxiliares y, a continuación, creamos una acción rápida para invocar el componente.

Superposición de acción de Lightning para SMS

Esta acción de Lightning consta de un componente Aura, un controlador de JavaScript y un controlador de Apex, el cual hace referencia a las clases de la biblioteca que procesan la integración con Twilio.

SendTwilioSMS.cmp

<aura:component controller="TwilioSendSMSController" implements="flexipage:availableForAllPageTypes,force:hasRecordId,force:lightningQuickAction" >
   <aura:attribute name="textMessage" type="String" />
   <aura:attribute name="destinationNumber" type="String" />
   <aura:attribute name="messageError" type="Boolean" />
   <aura:attribute name="recordId" type="String" />
      <aura:handler name="init" value="{!this}" action="{!c.init}" />
   <aura:if isTrue="{!v.messageError}">
      <!-- Load error -->
      <div class="userCreateError">
         <ui:message title="Error" severity="error" closable="true">
            Unable to send message. Please review your data and try again.
         </ui:message>
      </div>
   </aura:if>
   <div class="slds-form--stacked">
      <label class="slds-form-element__label" for="instructMsg">Please enter the message (max 160 char) below: </label>
      <br/>
      <div class="slds-form-element__control">
         <ui:inputText class="slds-input" aura:id="message" label="Text Message" value="{!v.textMessage}" required="true" maxlength="160" size="165" />
      </div>
      <div class="centered">
         <ui:button class="slds-button slds-button--brand" press="{!c.sendMessage}" label="Send Message"/>
      </div>
   </div>
</aura:component>

SendTwilioSmsController.js

({
   init : function(component, event, helper) {
      var action = component.get("c.getPhoneNumber");
      action.setParams({"contactId": component.get("v.recordId")});
      action.setCallback(this, function(response) {
         var state = response.getState();
         if(component.isValid() && state == "SUCCESS"){
            component.set("v.destinationNumber", response.getReturnValue());
         } else {
            component.set("v.messageError", true);
         }
      });
      $A.enqueueAction(action);
   },
      sendMessage : function(component, event, helper) {
      var smsMessage = component.get("v.textMessage");
      var number = component.get("v.destinationNumber");
      var recordId = component.get("v.recordId")
      var action = component.get("c.sendMessages");
      action.setParams({"mobNumber": number, "message": smsMessage, "contactId": component.get("v.recordId")});
      action.setCallback(this, function(response) {
         var state = response.getState();
         if(component.isValid() && state == "SUCCESS"){
            $A.get("e.force:closeQuickAction").fire();
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
               "title": "Success!",
               "message": "SMS has been sent woo hoo!"
            });
            toastEvent.fire();
         } else {
            component.set("v.messageError", true);
         }
      });
      $A.enqueueAction(action);
   }
})

SendTwilioSmsController.apxc

/*
* Apex controller that currently contains only one method to send sms message
*/
global class TwilioSendSMSController {
   /*
   * This method uses the Twilio for Salesforce library class and method to
   * send the message using the Twilio api.
   */
   @AuraEnabled
      webService static String sendMessages(String mobNumber, String message, Id contactId) {
         System.debug('the mobNumber is: '+ mobNumber + ' and the message is: '+ message + ' and contactId is: ' + contactId);
         if (mobNumber == null) {
            mobNumber = getPhoneNumber(contactId);
         }
         try {
            TwilioRestClient client = TwilioAPI.getDefaultClient();
            Map<String,String> params = new Map<String,String> {
               'To' => mobNumber,
               'From' => '15555551234',
               'Body' => message
               };
            TwilioSMS sms = client.getAccount().getSMSMessages().create(params);
            return sms.getStatus();
         } catch(exception ex) {
            System.debug('oh no, it failed: '+ex);
            return 'failed';
         }
      }
      @AuraEnabled
      public static String getPhoneNumber(Id contactId) {
         Contact currentRecord = [SELECT Phone FROM Contact WHERE Id = :contactId];
         return currentRecord.Phone.replace(' ', '').replace('-', '').replace(')', '').replace('(', '');
   }
}

Después de crear el componente, creamos una acción de Lightning. En esta ocasión, la agregamos al formato de página de contactos para que los agentes puedan acceder a la acción.

Una vez más, la parte interesante es que la acción también está disponible en la aplicación móvil Salesforce. Si un proveedor de servicios de vehículos intenta hacer contacto con el cliente en el aeropuerto, el conductor puede usar un dispositivo móvil para tener acceso al cliente fácilmente.

Acción SMS Lightning en la aplicación Salesforce

Las acciones de Lightning son el futuro de las acciones programáticas en Lightning Experience. Esperamos que empiece a considerar este conjunto de soluciones como una alternativa mejor a los botones de JavaScript.

Además, si depende de aplicaciones de socios que usan botones de JavaScript, le alegrará saber que muchos de nuestros socios ya han empezado a migrar y actualizar sus aplicaciones a Lightning. Encontrará más aplicaciones en AppExchange, las cuales están actualizadas para Lightning Experience.

Más allá de los fundamentos

Si desea probar Twilio por su cuenta, dispone de un paquete no gestionado de Twilio para la clase de la biblioteca de Salesforce usada en el ejemplo de integración con terceros en la biblioteca Github de https://github.com/twilio/twilio-salesforce

Antes de usar Twilio para Salesforce y la API de Twilio, cree una cuenta de Twilio, configure un número de teléfono y conecte su cuenta a Salesforce. Consulte TwilioApi.cls en la biblioteca Github para obtener instrucciones más detalladas.