Se você já atuou em um grande projeto de Lightning, deve estar cansado de repetir o mesmo trecho de código toda vez que precisa interagir com uma Controller, não é mesmo? Então hoje quero te mostrar duas coisas legais, a primeira é o que o título do post diz, estender um componente do Lightning, e o que isso significa, isso significa que você pode aproveitar parte de um componente dentro de outro, mas fique tranquilo, isso fará sentido assim que começarmos a colocar a mão na massa, e segundo vamos fazer isso criando um componente que permitirá você fazer chamadas Apex com apenas 1 linha de código.
Entendendo o problema
Como eu disse antes, se você já trabalhou com grandes projetos em Lightning, muito provavelmente já precisou fazer uma chamada Apex para consumir dados do Salesforce, e se fez isso mais de uma vez, então você deve ter escrito o código abaixo algumas vezes, não é?
var action = component.get("c.getAccounts"); action.setParams(params); action.setCallback(this, function(response) { var state = response.getState(); if (state === "SUCCESS") { var result = response.getReturnValue(); //do something with the result } else if (state === "INCOMPLETE") { // wait, what? } else if (state === "ERROR") { var errors = response.getError(); //do something about the errors } }); $A.enqueueAction(action);
E sim, isso é chato e repetitivo, e tudo que é repetitivo pode ser de alguma forma, otimizado e essa é a ideia aqui hoje!
O resultado esperado
Bom, para facilitar a nossa vida, não seria mais fácil se pudéssemos simplesmente chamar uma classe do apex assim:
helper.apexMethod(component, "getAccount", { id : accountId}) .then(function (result) { //Trate o result aqui! })
Ou ainda, imagine escrever a sua query SOQL diretamente no componente do Lightning e ter o resultado de uma forma mais simples impossível:
helper.SOQL(component, "SELECT Id, Name, AnnualRevenue, Industry FROM Account WHERE Name Id = '" + accountID + "'") .then(function (account) { //Trate a account aqui! })
Parece mágico, não? pois é exatamente isso que iremos fazer hoje, então chega de enrolação e vamos colocar a mão na massa.
Criando nossa Controller do Apex
A primeira coisa que vamos precisar, é uma Controller do Apex, capaz de falar com nosso componente do Lightning, e fazemos isso utilizando o annotation @AuraEnabled, o objetivo dessa nossa classe é ser um Helper para a execução de nossas futuras SOQLs diretamente de um componente Lightning, então nossa classe ficará assim:
public with sharing class ApexExtensionController { @AuraEnabled public static List<sObject> executeSOQL(String query) { return Database.query(query); } }
Como você pode ver, nossa classe é bem simples, o nosso método executeSOQL receberá a query, executar e devolver os dados para o Lightning, mas como toda classe precisa de uma cobertura de teste, eu não ia te deixar na mão né, mas isso você pode pegar direto no meu Github.
Criando o Lightning Component
Chegou a hora de criamos o nosso Lightning Component que será estendido futuramente, ele será o responsável por fazer o meio do caminho entre os seus componentes e o Apex, bom se você chegou até aqui, acredito que já deve ter criado o seu primeiro componente em Lightning, então vou ser mais direto ao assunto e pular direto para a parte prática, sem ficar demostrando passo a passo de itens de menu, mas se tiver dúvidas, fala ai nos comentários que eu te ajudo sem problemas, ok?
Então vamos lá, vou chamar o nosso componente de ApexExtension, nome bem criativo, eu sei, nosso .CMP ficará assim:
<aura:component extensible="true" abstract="true" controller="ApexExtensionController"> {!v.body} </aura:component>
Note um ponto interessante nesse componente, que é o extensible=”true”, isso indica que um componente do Lightning pode ser estendido por outros componentes, um outro atributo interessante aqui é o abstract=”true” isso garante a segurança do nosso componente, evitando que ele seja instanciado diretamente, até porque ele sozinho não servirá para nada não é mesmo?
O Helper do nosso componente ficará assim:
({ apexMethod: function (component, method, params) { return new Promise(function (resolve, reject) { var action = component.get("c." + method) action.setParams(params) action.setCallback(this, function (response) { var state = response.getState() if (state === "SUCCESS") { resolve(response.getReturnValue()) } else if (state === "INCOMPLETE") { // Really? Why?? } else if (state === "ERROR") { var errors = response.getError() if (errors && errors[0] && errors[0].message) { console.log("Error message: " + errors[0].message) reject(errors[0].message) } else { console.log("Unknown error") reject("Unknown error") } } }) $A.enqueueAction(action) }) }, SOQL: function (component, query) { return this.apexMethod(component, 'executeSOQL', {query: query}) } })
Com isso, temos tudo pronto por aqui, agora é hora de consumir o que criamos, vamos lá?
Estendendo componente do Lightning
Agora é a hora em que a mágica acontece, vamos criar um componente que irá listar todos os contados de uma conta usando o novo método SOQL e também chamar o método de uma controller para obter o total de contatos associados a uma conta, e para isso vamos estender o componente que criamos antes, e utilizar os métodos novos que ganhamos \o/. Vale ressalvar que existem maneiras mais fáceis de fazer isso, mas o objetivo aqui é demostrar o uso do nosso Extender.
Abaixo como ficará nosso novo componente ContactList:
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" extends="c:ApexExtension" controller="ContactController"> <aura:attribute name="recordId" type="String" /> <aura:attribute name="records" type="List" /> <aura:attribute name="totalRecords" type="Integer" /> <aura:handler name="init" value="{!this}" action="{!c.initHandler}" /> <div> <aura:iteration items="{!v.records}" var="contact"> <lightning:card title="{!contact.Name}"/> </aura:iteration> <div>Total Contacts: {!v.totalRecords}</div> </div> </aura:component>
Note o atributo extends=”c:ApexExtension” ele é responsável por estender o componente com base no nosso componente criado anteriormente.
E nossa controller do Lightning ficará assim:
({ initHandler : function(component, event, helper) { const recordId = component.get('v.recordId') const query = "SELECT Id, Name FROM Contact WHERE AccountId = '" + recordId + "'" //Executa uma consulta SOQL helper.SOQL(component, query).then(function(result){ component.set('v.records', result) }).catch(function (error) { console.error(error) }) const params = {'accountId': recordId} //Realiza uma chamada a um método do Apex helper.apexMethod(component, 'totalContacts', params).then(function(result) { component.set('v.totalRecords', result) }).catch(function (error) { console.error(error) }) } })
O nosso componente consome uma nova Apex Controller, quer será responsável por retornar o número total de contatos de uma conta, a nossa controller ficará assim:
public with sharing class ContactController { @AuraEnabled public static Integer totalContacts(Id accountId) { return [SELECT COUNT() FROM Contact WHERE AccountId =: accountId]; } }
Todos os códigos utilizados para esse exemplo estão disponíveis nesse Github.
Espero que isso os ajude a escrever componentes do Lightning com menos linhas e mais organizado.
Um forte abraço Trailblazers, e até o próximo post :)
Fernando Sousa
Senior Salesforce Developer
Bacharel em Sistemas da Informação pela Universidade de Taubaté (UNITAU) e MBA em Projeto de Aplicações para Dispositivos Móveis pelo IGTI – Instituto de Gestão em Tecnologia da Informação.
Comecei a programar bem cedo, por volta de 10 anos de idade, de maneira auto-didata passei por várias linguagens.
Em 2015 me conectei a plataforma Salesforce pela primeira vez, para fazer una integração entre um Aplicativos Mobile em android e o Salesforce Platform.
Atualmente com as certificações Salesforce Certified Platform Developer I, Salesforce Certified Platform App Builder, Salesforce Certified Platform Developer II, Salesforce Administrator e Sharing and Visibility.
Acompanhe meu Trailhead aqui.