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

Conectar con Salesforce con controladores del lado del servidor

Objetivos de aprendizaje

Después de completar esta unidad, podrá:
  • Crear métodos de Apex a los que se puede llamar de forma remota desde código de componentes Aura.
  • Realizar llamadas desde componentes Aura a métodos remotos.
  • Gestionar respuestas de servidores de forma asíncrona empleando funciones de devolución de llamadas.
  • Objetivo ampliado: explicar la diferencia entre “c.”, “c:” y “c.”.

Controladores de servidor

Hasta ahora todo lo que hicimos fue estrictamente en el lado del cliente. Aún no estamos guardando nuestros gastos en Salesforce. Cree algunos gastos y luego vuelva a cargar, ¿qué ocurre? Correcto, todos los gastos desaparecen. ¡Bieeen, dinero gratis!

Lo malo es que llamaron los contadores y bueno, no tienen ningún sentido del humor con estas cosas. Y realmente, ¿no queremos que se nos reembolsen esos gastos que están saliendo de nuestros bolsillos? ¡Vaya! ¡Guardar sus datos en Salesforce es un error P0 seguro!

Bromas aparte, llegó el momento de incorporar controladores del lado del servidor a nuestra aplicación. Hemos estado retrasando esto mientras asimilábamos los fundamentos. Ahora que estamos listos, ¡vamos a ello!

Veamos algunas imágenes. Asegurémonos de que tenemos la ruta correcta y que el depósito está lleno antes de salir a la carretera.

En primer lugar, repasemos el primer diagrama que vimos en este módulo, una visión de (muy) alto nivel de la arquitectura de las aplicaciones Componentes Lightning.

Una arquitectura de muy alto nivel de Componentes Lightning: vista de cliente y controlador, controlador y base de datos de Apex de servidor

Hasta ahora, todo lo que vimos ha estado en el lado del cliente de esta imagen. (Y tenga en cuenta que simplificamos combinando controladores y auxiliares aquí.) Aunque hicimos referencia a un tipo de objeto personalizado, Expense__c, que está definido en el lado del servidor, realmente nunca tocamos el servidor directamente.

¿Recuerda cómo hablamos de conectar diferentes elementos entre sí para crear un circuito completo? El formulario de gastos que creamos en la última unidad podría tener el siguiente aspecto.

Lado del cliente del flujo

El circuito comienza con el botón Create (Crear), que está conectado al controlador de acciones clickCreate (1). Cuando el controlador de acciones se ejecuta, obtiene valores de los campos de formulario (2) y luego agrega un nuevo gasto a la matriz expenses (3). Cuando la matriz se actualiza a través de set, desencadena la representación automática de la lista de gastos (4), completando el circuito. Sencillo, ¿verdad?

Bien, cuando conectamos en el acceso del lado del servidor, el diagrama se complica un poco. ¡Más flechas, más colores, más números! (Pospondremos la explicación de todo ello por el momento.)

Flujo completo: lado del cliente y del servidor

Lo que es más, este circuito no tiene el mismo flujo de control suave y síncrono. Las llamadas de servidor son caras, y pueden tardar un poco de tiempo. Milisegundos cuando las cosas están bien, y largos segundos cuando la red está congestionada. Usted no quiere que las aplicaciones se bloqueen esperando respuestas de servidor.

La solución para mantener la capacidad de respuesta mientras se espera es que las respuestas de servidor se gestionan de forma asíncrona. Lo que esto significa es que cuando hace clic en el botón Create Expense, su controlador del lado del cliente desencadena una solicitud de servidor y sigue procesando. No solo no espera al servidor, ¡olvida que realizó la solicitud!

Luego, cuando la respuesta vuelve del servidor, el código que estaba empaquetado con la solicitud, llamado una función de devolución de llamadas, ejecuta y gestiona la respuesta, incluyendo la actualización de los datos del lado del cliente y la interfaz de usuario.

Si usted es un programador de JavaScript experimentado, la ejecución asíncrona y las funciones de devolución de llamadas son el pan de cada día. Si no ha trabajado con ellas antes, esto va a ser nuevo y quizá bastante diferente. También es muy interesante.

Consulta de datos desde Salesforce

Vamos a empezar a leer datos desde Salesforce, para cargar la lista de gastos existentes cuando se inicia la aplicación Expenses.

Nota

Nota

Si aún no creó algunos registros de gastos reales en Salesforce, ahora sería un buen momento. En caso contrario, después de implementar lo que sigue, podría tardar tiempo en depurar por qué no se carga nada, cuando realmente no hay nada que cargar. Su humilde autor queda rendido ante esto En. Todo. Momento.

El primer paso es crear su controlador Apex. Los controladores Apex contienen métodos remotos que sus Componentes Lightning pueden llamar. En este caso, para consultar y recibir datos de gastos desde Salesforce.

Echemos un vistazo a una versión simplificada del código. En Developer Console, cree una nueva clase de Apex denominada “ExpensesController” y pegue el código siguiente.

public with sharing class ExpensesController {
    // STERN LECTURE ABOUT WHAT'S MISSING HERE COMING SOON
    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }
}

Trataremos los controladores de Apex con mayor profundidad en la siguiente sección, pero por ahora este es un método de Apex bastante sencillo. Ejecuta una consulta SOQL y devuelve los resultados. Hay solo dos cosas específicas que hacen que este método esté disponible para su código de Componentes Lightning.

  • La anotación @AuraEnabled antes de la declaración del método. “Aura” es el nombre del marco de trabajo del núcleo de los Componentes Lightning. Lo vio empleado en el espacio de nombres de algunas de las etiquetas de núcleo, como <aura:component>. Ahora ya sabe de dónde viene.
  • La palabra clave static. Todos los métodos de controlador @AuraEnabled deben ser métodos estáticos, y del ámbito public o global.

Si estos requisitos le recuerdan métodos remotos para la función de comunicación remota JavaScript de Visualforce, no es pura coincidencia. Los requisitos son los mismos, porque la arquitectura es muy similar en los puntos clave.

Otra cosa desdeñable es que el método no hace nada especial para empaquetar los datos para Componentes Lightning. Solo devuelve los resultados de la consulta SOQL directamente. El marco de los Componentes Lightning gestiona todo el trabajo de clasificación/desclasificación aparejado en la mayoría de las situaciones. ¡Excelente!

Carga de datos desde Salesforce

El siguiente paso es conectar el componente expenses con el controlador de Apex del lado del servidor. Esto es tan fácil que podría sentirse aturdido. Cambie la etiqueta <aura:component> de apertura del componente expenses para que apunte al controlador de Apex de este modo.

<aura:component controller="ExpensesController">

La nueva parte está resaltada en negrita y sí, es así de sencillo.

No obstante, apuntar al controlador de Apex no carga realmente ningún dato ni llama al método remoto. Como la conexión automática entre el componente y el controlador (del lado del cliente), este apuntamiento simplemente permite que estas dos partes se “conozcan” entre sí. Este “conocimiento” incluso toma la misma forma, otro proveedor de valores, que veremos en un momento. Pero la conexión automática solo llega hasta ahí. Sigue siendo nuestro trabajo completar el circuito.

En este caso, completar el circuito implica lo siguiente.

  1. Cuando el componente expenses se carga:
  2. Consulta los registros expenses existentes en Salesforce y
  3. Agregue esos registros al atributo del componente expenses.

Trataremos cada uno de ellos por orden. El primer elemento, el comportamiento del desencadenamiento cuando se carga por primera vez el componente expenses requiere que redactemos un gestor de inicialización. Esto es solo una abreviatura para un gestor de acciones que está conectado con el evento init de un componente, que ocurre cuando el componente se crea por primera vez.

La conexión que necesita para este es una única línea de marca. Agregue lo siguiente al componente expenses, justo debajo de las definiciones de atributos del componente.

<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

La etiqueta <aura:handler> es el modo de decir que un componente puede, bueno, gestiona un evento específico. En este caso estamos diciendo que gestionaremos el evento init y que lo gestionaremos con el gestor de acciones doInit en nuestro controlador. (El establecimiento de value="{!this}" marca este como un “evento del valor”. Lo que esto significa es demasiado complejo para explicarlo aquí. Solo tiene que saber que debe agregar siempre este par atributo-valor o a un evento init.)

Llamada a métodos del controlador del lado del servidor

Un paso más, quedan dos. Ambos pasos restantes se llevan a cabo en el gestor de acciones doInit, así que echémosle un vistazo. Agregue el siguiente código al controlador del componente expense.

    // Load expenses from Salesforce
    doInit: function(component, event, helper) {
        // Create the action
        let action = component.get("c.getExpenses");
        // Add callback behavior for when response is received
        action.setCallback(this, function(response) {
            let state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.expenses", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        // Send action off to be executed
        $A.enqueueAction(action);
    },

Antes de que se pierda con todas las cosas nuevas aquí, tenga en cuenta que este es solo otro gestor de acciones. Tiene el mismo formato, y la firma de la función es la misma. Estamos en territorio conocido.

Dicho esto, cada línea de código después de la firma de la función es nueva. Las trataremos todas en un momento, pero queremos esbozar lo que esta línea de código hace:

  1. Crea una llamada de método remoto.
  2. Configura lo que debería ocurrir cuando vuelve la llamada de método remoto.
  3. Pone en cola la llamada de método remoto.

Parece bastante sencillo, ¿verdad? Quizá la estructura o los aspectos específicos del código sean nuevos, pero los requisitos básicos de lo que tiene que suceder son de nuevo familiares.

¿Suena eso a que estamos siendo muy esperanzadores? Bueno, quizá estemos intentando dirigirle por terrenos escabrosos. De acuerdo, tenemos que hablar de algo. El problema aparece en la primera línea de código de la función.

        let action = component.get("c.getExpenses");

Esta línea de código crea nuestra llamada de método remoto, o acción remota. Al principio la parte component.get() parece familiar. Hicimos esto muchas veces hasta este punto.

Excepto… bueno, antes era “v.algo” lo que estábamos obteniendo, siendo v el proveedor de valores para la vista. Aquí es “c”, y sí, c es otro proveedor de valores. Y antes vimos un proveedor de valores c, en expresiones como press="{!c.clickCreate}” y action="{!c.doInit}".

Estas expresiones estaban en marca de componentes en la vista. Aquí en el controlador, el proveedor de valores c representa algo diferente. Representa el controlador de Apex remoto.

“Esperen un minuto. ¿Me están diciendo que tenemos el controlador del lado del cliente c, c el espacio de nombres predeterminado y c el controlador del lado del servidor, todos en componentes Aura?”

Bueno, en una palabra, sí. Respiremos hondo.

Mire, le seremos sinceros. Si tuviéramos que hacer todo de nuevo, probablemente habríamos realizado otras elecciones. Aunque las elecciones que realizamos no fueron accidentes, tres “c” son definitivamente una oportunidad para la confusión. ¡Nosotros también nos confundimos!

Pero como se dice, es lo que hay. Hombre prevenido vale por dos. Ahora lo sabe.

Identificador
Contexto
Significado
c.
Marca de componentes
Controlador del lado del cliente
c.
Código de controlador
Controlador del lado del servidor
c:
Marca
Espacio de nombres predeterminado

De acuerdo, volvamos a nuestro código. Antes de quedarnos frenados estábamos mirando esta línea.

        let action = component.get("c.getExpenses");

Donde, en el código anterior, component.get("v.algo") nos devolvía una referencia a un componente secundario en la vista (marca de componente), component.get("c.loquesea") devuelve una referencia a una acción disponible en el controlador. En este caso, devuelve una llamada de método remoto a nuestro controlador de Apex. Así es como crea una llamada a un método @AuraEnabled.

La siguiente “línea”, action.setCallback(…), es un bloque de código que se ejecutará cuando vuelva la llamada de método remoto. Debido a que esto ocurre “después”, dejémosla a un lado por el momento.

La siguiente línea que realmente se ejecuta es esta.

        $A.enqueueAction(action);

Vimos $A brevemente antes, pero no la tratamos. Es una variable global de marco que proporciona un número de importantes funciones y servicios. $A.enqueueAction(action) agrega la llamada del servidor que acabamos de configurar a la cola de solicitudes del marco de componentes Aura. Esta, junto con otras solicitudes de servidor pendientes, se enviará al servidor en el siguiente ciclo de solicitudes.

Eso no suena muy preciso. Los detalles completos son interesantes e importantes para el uso avanzado de componentes Aura. Pero por ahora, esto es lo que necesita saber sobre $A.enqueueAction(action).

  • Pone en cola la solicitud del servidor.
  • En todo lo que se refiere a su acción de controlador, llega hasta aquí.
  • No tiene garantías de cuándo o cómo volverá a aparecer.

Aquí es donde ese bloque de código que pusimos a un lado vuelve a entrar. Pero antes de que hablemos de eso, un poco de cultura pop.

Llamadas de servidor, ejecución asíncrona y funciones de devolución de llamadas

El single de Carly Rae Jepsen “Call Me Maybe” se publicó en 2011 y alcanzó un gran éxito comercial y de crítica, llegando al Nº1 en más de una docena de países. Hasta la fecha ha vendido más de 18 millones de copias en todo el mundo y es, aparentemente, uno de los singles digitales de mayores ventas de la historia. La frase más memorable, del estribillo, es “Here’s my number. So call me maybe.” (Aquí tienes mi número, llámame si quieres). Además de optimista y peligrosamente pegadiza, es una metáfora de cómo los componentes Aura gestionan las llamadas de servidor.

Escúchenos. Echemos un vistazo a nuestro gestor de acciones en pseudocódigo.

    doInit: function(component, event, helper) {
        // Load expenses from Salesforce
        let action = component.get("c.getExpenses");
        action.setCallback(
            // Here’s my number,
            // Call me maybe
        );
        $A.enqueueAction(action);
    },

Vaya... Quizá deberíamos explicar los parámetros de action.setCallback() con mayor detalle. En el código del gestor de acciones real, lo llamamos así.

        action.setCallback(this, function(response) { ... });

this es el ámbito en el que se ejecutará la devolución de llamadas; aquí this es la función del gestor de acciones en sí. Piense en él como una dirección, o quizá un número. La función es a lo que se llama cuando se devuelve la respuesta del servidor. Así que:

        action.setCallback(scope, callbackFunction);

Here’s my number. Call me maybe. (Aquí tienes mi número, llámame si quieres).

El efecto global es crear la solicitud, empaquetar el código para lo que hay que hacer cuando se realice la solicitud y enviarla para su ejecución. En ese punto, el gestor de acciones detiene su ejecución.

Aquí hay otra forma de entenderlo. Podría preparar a su hijo para la escuela y pasarle una lista de las tareas que desea que haga cuando vuelva a casa tras las clases. Lo deja en el colegio y luego se va a trabajar. Mientras está trabajando, usted está realizando su trabajo, tranquilo en el conocimiento que su hijo, que es muy bueno, hará el trabajo que le asignó cuando vuelva de la escuela. Usted no hace ese trabajo, y no sabe cuándo se realizará exactamente. Pero se hace.

Aquí hay una última manera de presentarlo, de nuevo en pseudocódigo. Esta versión “desembala” la función de devolución de llamadas para mostrar una versión más lineal del gestor de acciones.

    // Not real code! Do not cut-and-paste!
    doInit: function(component, event, helper) {
        // Create server request
        let action = component.get("c.getExpenses");
        // Send server request
        $A.enqueueAction(action);
        // ... time passes ...
        // ...
        // ... Jeopardy theme plays ...
        // ...
        // ... at some point in the indeterminate future ...
        // Handle server response
        let state = action.response.getState();
        if (state === "SUCCESS") {
            component.set("v.expenses", action.response.getReturnValue());
        }
    },

Lo diremos de nuevo. Las ejecuciones asíncronas y las funciones de devolución de llamadas son de rigor para los programadores de JavaScript, pero si vienen de otras disciplinas, podrían estar menos familiarizados. Con un poco de suerte lo habrán comprendido en este punto, porque es fundamental en el desarrollo de aplicaciones con Componentes Lightning.

Gestión de la respuesta del servidor

Ahora que tenemos la estructura para crear una solicitud de servidor, miremos los detalles del método para gestionar la respuesta por parte de nuestra función de devolución de llamadas. Aquí está solo la función de devolución de llamadas.

    function(response) {
        let state = response.getState();
        if (state === "SUCCESS") {
            component.set("v.expenses", response.getReturnValue());
        }
    }

Las funciones de devolución de llamadas toman un único parámetro, response, que es un objeto opaco que proporciona los datos devueltos, si los hubiera, y varios detalles sobre el estado de la solicitud.

En esta función de devolución de llamadas específica, hacemos lo siguiente.

  1. Obtener el estado de la respuesta.
  2. Si el estado es SUCCESS y nuestra solicitud se completó como estaba planeado, entonces:
  3. Establecer el atributo expenses del componente en el valor de los datos de la respuesta.

Probablemente tenga algunas preguntas, como:

  • ¿Qué ocurre si el estado de la respuesta no es SUCCESS?
  • ¿Qué ocurre si nunca llega la respuesta? (Call me maybe.)
  • ¿Cómo podemos solo asignar los datos de la respuesta a nuestro atributo de componente?

Las respuestas a las dos primeras son que, desgraciadamente, no vamos a tratar esas posibilidades en este módulo. Ciertamente hay cosas que necesita saber y tener en cuenta en sus aplicaciones del mundo real, pero simplemente no tenemos sitio.

La última pregunta es la más relevante aquí, pero también la más sencilla de responder. Definimos un tipo de datos para el atributo expenses.

<aura:attribute name="expenses" type="Expense__c[]"/>

Y nuestra acción del controlador del lado del servidor tiene una firma de método que define su tipo de datos de devolución.

public static List<Expense__c> getExpenses() { ... }

Los tipos coinciden, así que solo tenemos que asignar uno al otro. Los componentes Aura gestionan los detalles. Ciertamente puede hacer su propio procesamiento de los resultados, y convertirlos en otros datos dentro de su aplicación. Pero si diseña adecuadamente sus acciones del lado del servidor, no tiene necesariamente que hacerlo.

De acuerdo, esas fueron un montón de maneras diferentes de mirar docenas de líneas de código. Aquí está la pregunta: ¿Ha probado su versión de nuestra aplicación con el código? Porque terminamos con la carga de gastos desde la parte de Salesforce. ¡Vuelva a cargar la aplicación y vea si los gastos que ingresó en Salesforce aparecen!

Controladores de Apex para componentes Aura

Antes de que realicemos el siguiente paso en el desarrollo de la aplicación, profundicemos un poco más en ese controlador Apex. Aquí tenemos un vistazo de la siguiente versión, que tendremos que gestionar creando nuevos registros, así como actualizando la casilla de verificación Reimbursed? en registros existentes.

public with sharing class ExpensesController {
    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        // Perform isAccessible() checking first, then
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }
    @AuraEnabled
    public static Expense__c saveExpense(Expense__c expense) {
        // Perform isUpdateable() checking first, then
        upsert expense;
        return expense;
    }
}

La versión anterior prometía un severo sermón, que ya viene. Pero primero, centrémonos en los detalles de esta versión mínima.

En primer lugar, solo agregamos un nuevo método @AuraEnabled, saveExpense(). Toma un objeto Expense (Expense__c) y lo altera. Esto nos permite utilizarlo para crear nuevos registros y actualizar registros existentes.

A continuación, observe que creamos la clase con las palabras clave with sharing. Esto se aplica automáticamente las reglas de colaboración de su organización al registro que está disponible a través de esos métodos. Por ejemplo, los usuarios normalmente verían únicamente sus propios registros de gastos. Salesforce gestiona todas las complicadas reglas SOQL por usted de forma automática en segundo plano.

El uso de las palabras clave with sharing es una de las medidas de seguridad esenciales que necesita tomar cuando redacta código de controladores del lado del servidor. No obstante, es una medida que es necesaria, pero no suficiente. ¿Ve los comentarios sobre la realización de comprobaciones isAccessible() y isUpdateable()? with sharing solo le lleva hasta ahí. En particular, tiene que implementar seguridad a nivel de campo y objeto (que verá abreviada frecuentemente como FLS) usted mismo.

Por ejemplo, aquí hay una versión de nuestro método getExpenses() con esta seguridad mínimamente implementada.

    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        // Check to make sure all fields are accessible to this user
        String[] fieldsToCheck = new String[] {
            'Id', 'Name', 'Amount__c', 'Client__c', 'Date__c',
            'Reimbursed__c', 'CreatedDate'
        };
        Map<String,Schema.SObjectField> fieldDescribeTokens =
            Schema.SObjectType.Expense__c.fields.getMap();
        for(String field : fieldsToCheck) {
            if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
                throw new System.NoAccessException();
            }
        }
        // OK, they're cool, let 'em through
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }

Es una expansión bastante grande de nuestro código de una línea inicial, y también es adecuado. Del mismo modo, las llamadas describe son caras. Si su aplicación está llamando a este método con frecuencia, debería encontrar una manera de optimizar o colocar en caché sus comprobaciones de acceso por usuario.

Como con SLDS, simplemente no tenemos el espacio para impartir todos los detalles de la codificación segura de Apex. A diferencia de SLDS, tomar la responsabilidad de la seguridad del código que redacta no es opcional. Si no leyó los artículos sobre prácticas de codificación segura en los Recursos, póngalos en su cola.

De acuerdo, </severo-sermón>.

Guardar datos en Salesforce

Antes de que implementemos el formulario Add Expense de verdad, sin hacer trampas, veamos primero cómo crear un nuevo registro es un reto diferente al de leer registros existentes. Con doInit(), simplemente leemos algunos datos y luego se actualiza la interfaz de usuario de la aplicación. Bastante sencillo, incluso teniendo que hablar de Carly Rae en la explicación.

La creación de un nuevo registro es más complicado. Vamos a leer valores desde el formulario, crear un nuevo registro de gasto localmente, enviar el registro para que se guarde en el servidor y luego, cuando el servidor nos diga que está guardado, actualizar la interfaz de usuario empleando el registro devuelto desde el servidor.

¿No le parece que suena a realmente complicado? ¿Es posible que necesitemos a los Rolling Stones y un álbum completo de canciones para ayudarnos con la siguiente explicación?

Echemos un vistazo a algo de código y luego decida por usted mismo.

En primer lugar, asegúrese de que guardó y actualizó la versión del controlador de Apex, la versión precedente que incluye el método saveExpense().

¿Recuerda que le mostramos cómo controlar el envío de un formulario? Si tan solo uno de los campos no es válido, se muestra un mensaje de error y el formulario no se envía. El mensaje de error se elimina cuando todos los campos son válidos.

Debido a que ponemos todos los detalles de la creación de un nuevo gasto (y todas las trampas) en la función auxiliar createExpense(), no es necesario realizar ningún cambio adicional en el controlador. ¿Fácil hasta ahora?

De este modo, todo lo que tenemos que hacer es cambiar la función createExpense() en el auxiliar para que realice todas esas cosas complicadas que mencionamos anteriormente. Este es el código.

    createExpense: function(component, expense) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
        $A.enqueueAction(action);
    },

¿Es tan complicado como se esperaba? ¿Con tantas líneas? ¡Esperamos que no!

En realidad, solo hay una cosa nueva en este gestor de acciones, y es fácil de comprender. Pasemos por el código.

Empezamos creando la acción con component.get("c.saveExpense") obteniendo el nuevo método del controlador de Apex. Muy familiar.

A continuación adjuntamos una carga de datos a la acción. Esto es nuevo. Tenemos que enviar los datos para el nuevo gasto al servidor. ¡Pero mire qué fácil es! Solo hay que utilizar action.setParams() y proporcionar un objeto de estilo JSON con pares de valores nombre parámetro-parámetro. Un truco, y es importante, es que su nombre de parámetro debe coincidir con el nombre del parámetro empleado en su declaración de método de Apex.

A continuación establecemos la devolución de llamada para la solicitud. De nuevo, esto es lo que ocurrirá cuando el servidor devuelva una respuesta. Si compara esta función de devolución de llamada con nuestra función auxiliar createExpense original, es virtualmente idéntica (menos el truco detestable).

Justo como en la versión anterior, obtenemos con get() el atributo expenses, trasladamos con push() un valor dentro de él y luego lo establecemos con set(). La única diferencia real es que en vez de trasladar con push() nuestra versión local del nuevo gasto a la matriz, estamos trasladando con push()la respuesta del servidor.

¿Por qué funciona esto? Debido a que el método del lado del servidor altera el (en este caso nuevo) registro, marcándolo con un Id. y luego devuelve el registro resultante. Una vez más los tipos de datos del lado del cliente y del lado del servidor coinciden, de modo que no tenemos que realizar tareas adicionales.

Bueno, eso es todo. ¡No hicieron falta los Rolling Stones!

Cosas con las que tener cuidado

Aunque tratamos todos los aspectos básicos de la conexión de su código de componentes Aura del lado del cliente con código Apex del lado del servidor, hay un par de cosas que hay que dejar claras antes de que le estallen en la cara.

El primer problema es la distinción entre mayúsculas y minúsculas, y que se reduce a Apex y Salesforce en general que no distinguen entre mayúsculas y minúsculas, pero JavaScript distingue entre mayúsculas y minúsculas. Por ello, “Nombre” y “nombre” es lo mismo en Apex, pero no en JavaScript.

Esto llevará a fallos que le volverán absolutamente loco y que son completamente invisibles a sus ojos, incluso cuando están justo delante de sus ojos. Especialmente si estuvo trabajando con código que no sea de Componentes Lightning en Salesforce durante un tiempo, podría haber dejado de pensar sobre el uso de las mayúsculas y las minúsculas de los nombres de objetos y campos, métodos, etcétera.

Así que aquí tiene una mejor práctica: Utilice siempre el nombre de API exacto para cada objeto, campo, tipo, case, método, entidad, elemento, elefante, o lo que quiera que tenga. Siempre, en todo lugar, incluso cuando no importe. De esa manera, nunca tendrá problemas. O al menos, no este problema.

El otro problema que queremos resaltar es la naturaleza de “required” (obligatorio). No podemos resistir repetir una cita famosa: “Sigues empleando esa palabra. No creo que significa lo que tú crees que significa.”

En el código que redactamos hasta ahora vimos al menos dos tipos diferentes de “required”. En la marca del formulario Add Expense, verá la palabra utilizada de dos maneras. Por ejemplo, en el campo de nombre de expense.

<lightning:input aura:id="expenseform"
                 label="Expense Name"
                 name="expensename"
                 value="{!v.newExpense.Name}"
                 required="true"/> 

La etiqueta <lightning:input> incluye el atributo required establecido en true. Estos ejemplos ilustran solo un significado de required, que es “establecer la interfaz de usuario de este elemento para indicar que el campo es obligatorio”. En otras palabras, esto es solo cosmético. No hay aquí protección para la calidad de sus datos.

Otro significado de la palabra “required” se ilustra en la lógica de validación que redactamos para el mismo campo.

let validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
    // Displays error messages for invalid fields
    inputCmp.showHelpMessageIfInvalid();
    return validSoFar && inputCmp.get('v.validity').valid;
}, true);

La palabra “required” no aparece por ninguna parte, pero eso es lo que la lógica de validación hace valer. Debe establecer un valor para el campo de nombre de expense.

Y en su medida, esto es excelente. Su formulario de gastos no enviará un nuevo gasto con un nombre vacío. A no ser que haya un fallo. O quizá algún otro widget utiliza su mismo controlador del lado del servidor, pero no realiza esta validación de formulario con tanto cuidado. Etcétera. De este modo, esto es algo de protección para la calidad de sus datos, pero no es perfecta.

¿Cómo hace valer, y hacemos hincapié en hacer valer una regla de integridad de datos sobre el nombre de expense en este ejemplo? Se hace en el lado del servidor. Y no en cualquier parte del lado del servidor. La regla se pone en la definición del campo, o la codifica en un desencadenador. O bien, si usted es el tipo de ingeniero de correa y tirantes, como todos los buenos ingenieros, en ambos puntos.

Para una auténtica integridad de los datos, cuando “required” significa obligatorio, hágala valer al nivel más bajo posible.