Estendendo componentes do Lightning

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 BuilderSalesforce Certified Platform Developer IISalesforce Administrator e Sharing and Visibility.

Acompanhe meu Trailhead aqui.

 

 

 

 

ApexHelperLightningLightning ComponentsSalesforce AdvancedSalesforce IntermediateSalesforceDeveloperSOQL