Tratar de ações com controladores

Objetivos de aprendizagem

Após concluir esta unidade, você estará apto a:
  • Criar um controlador do lado do cliente para lidar com as ações do usuário.
  • Ler valores dos atributos do componente.
  • Ler valores dos controles da interface de usuário no seu componente.
  • Escrever um código de controlador em JavaScript que altera a interface de usuário.

Lidar com ações com controladores

Até agora, trabalhamos apenas com marcação no estilo XML. Até agora, a única forma de alterar a saída de nosso componente era alterar essa marcação. Até agora, nossos componentes não reagiam a entradas do usuário. Até agora, nós não escrevemos nenhum código JavaScript.

Isso tudo vai mudar nessa unidade.

Para começar, vamos ver um componente muito simples e imaginar o que ele precisa fazer para conseguir lidar com seu comportamento simples.

Mensagem do dia: Botão Você está ótimo hoje, botão Hoje vai ser um ótimo dia

Esta é a helloMessageInteractive, e é difícil imaginar um componente mais simples que “faça alguma coisa”. É um pedaço de texto estático, uma mensagem (atualmente em branco) e dois botões. Veja o código:

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Message of the day: {!v.message}</p>
    <div>
        <lightning:button label="You look nice today."
            onclick="{!c.handleClick}"/>
        <lightning:button label="Today is going to be a great day!"
            onclick="{!c.handleClick}"/>
    </div>
</aura:component>

Ele deve ser familiar. Tudo que fizemos, na verdade, foi adicionar dois componentes <lightning:button> a helloMessage. Quando você clica em um botão, a mensagem do dia é atualizada.

Bom, ainda não exatamente. Se você já digitou o código e tentou por si próprio, você percebeu que quando você clica em qualquer um dos botões, aparece uma mensagem de erro.

Há um problema

Nós somos os primeiros a admitir que nem todas as mensagem de erro nos componentes do Lightning são tão úteis quanto você gostaria que fossem. Mas essa é! Ela diz que não há ação do controlador nomeada de “handleClick”. De onde vem o “handleClick”? Ele vem de uma expressão que atribuímos ao atributo onclick em cada uma das duas marcas <lightning:button>:

onclick="{!c.handleClick}"

Considerando que isso é um botão, você provavelmente pode adivinhar que o atributo onclick é a forma de atribuir um comportamento ao botão para quando ele for clicado. Mas o que nós atribuímos? Uma expressão, {!c.handleClick}, que talvez seja um pouco misteriosa.

Na verdade, é muito simples. Assim como a expressão v.message de antes, c.handleClick é um provedor de valor, c, com uma propriedade, handleClick. c é o provedor de valor para o controlador do lado do cliente do componente, e handleClick é uma função definida nesse controlador. Então {!c.handleClick} é uma referência a um manipulador de ação no controlador do componente.

c.handleClick: c é o fornecedor de valor para o controlador do componente, com a propriedade handleClick, uma função definida nesse controlador

Ahn, o que é um controlador?

Oooops! Um controlador é basicamente uma coleção de códigos que definem o comportamento do seu aplicativo quando “coisas acontecem”, em que “coisas” quer dizer entradas do usuário, tempo e outros eventos, atualizações de dados e assim por diante. Se você procurar por “Modelo-Visualização-Controlador” em vários sites de desenvolvedor, verá várias definições. Para o que estamos fazendo, para os componentes do Lightning, um controlador é um recurso em um pacote de componente que mantém os manipuladores de ação para aquele componente. E manipuladores de ação são apenas funções de código JavaScript com uma assinatura de função particular.

Ir além das noções básicas

Falamos muito sobre controladores nessa unidade, e sabemos que o componente em si é uma visualização. Até mencionamos o padrão de design MVC, ou Modelo-Visualização-Controlador, que é tão comum em estruturas de aplicativos da web. Os componentes do Lightning são construídos em cima do padrão MVC?

Em uma palavra, não. Com certeza, existem semelhanças, mas seria mais correto dizer que os componentes do Lightning são Visualização-Controlador-Controlador-Modelo, ou talvez Visualização-Controlador-Controlador-Banco de dados.

Por que o “controlador” aparece duas vezes no nome do padrão? Porque ao interagir com o Salesforce, seus componentes sempre terão um controlador do lado do servidor além do controlador do lado do cliente com o qual trabalhamos nessa unidade. Este design de controlador duplo é a principal diferença entre os componentes do Lightning e MVC.

Qual é a diferença entre “modelo” e “banco de dados”? Em um MVC tradicional, o modelo é uma abstração programática (normalmente, uma classe) entre o armazenamento de dados subjacente (normalmente, um banco de dados relacional) e o restante do aplicativo. Nos componentes do Lightning, nenhuma classe do Apex fica diretamente entre os métodos do controlador @AuraEnabled e as operações DML. Mas, novamente, os sObjects já são uma abstração entre seu código do Apex e a camada de armazenamento subjacente. É possível adicionar campos de cálculo, lógica de validação e até mesmo adicionar um comportamento totalmente programático na forma de acionadores. Então, é um banco de dados ou um modelo? Nós falamos biscoito, mas não tem problema nenhum se você quiser chamar de bolacha.

Confuso? Animado? Vamos explicar melhor os detalhes dos controladores do lado do servidor em uma unidade posterior.

Vamos ver o controlador helloMessageInteractive com mais detalhe e tornar a explicação um pouco mais concreta.

({
    handleClick: function(component, event, helper) {
        let btnClicked = event.getSource();         // the button
        let btnMessage = btnClicked.get("v.label"); // the button's label
        component.set("v.message", btnMessage);     // update our message
    }
})

Os recursos do controlador têm um formato interessante. Eles são objetos em código JavaScript que contêm um mapa dos pares nome-valor, onde o nome é o nome do manipulador de ação e o valor é uma definição de função.

Manipuladores de ação

A combinação do par nome-valor e a assinatura de função específica é um manipulador de ação. Você vai ver ou ouvir os termos manipulador de ação, ação do controlador e função do controlador usados de maneira intercambiável, e na maioria das vezes isso está correto. Quase sempre eles querem dizer a mesma coisa. (Neste módulo não vamos nos preocupar com as exceções.)

Não se preocupe muito com o formato especial do recurso do controlador. Quando você clica no botão CONTROLADOR no Console do desenvolvedor, verá um recurso do controlador com um manipulador de ação de exemplo já adicionado. O único truque é que você precisa colocar vírgulas entre os manipuladores de ação – se esquecer, vai criar erros de sintaxe. Isso é a sintaxe básica do JavaScript, e vamos ver os detalhes daqui a pouco.

A função handleClick em si só tem quatro linhas de código, mas elas podem parecer difíceis de entender a princípio. Em um nível mais alto, é simples: Quando o botão é clicado, o manipulador de ação é chamado (1). No manipulador de ação, o controlador pega o botão que foi clicado, retira dele o texto de rótulo e depois define o atributo de message do componente para aquele texto (2). E a mensagem do dia é atualizada (3). Hoje será um ótimo dia!

No manipulador de ação, o controlador pega o texto do botão clicado e define o atributo de mensagem do componente

Simples, não é? Bom...

Como isso é super importante, vamos detalhar linha por linha.

handleClick: function(component, event, helper) {

O nome do manipulador de ação, seguido por uma declaração de função anônima. O mais importante aqui é a assinatura da função. Mesmo que não seja tecnicamente necessário, você sempre deve declarar suas funções do controlador para que assumam esses três parâmetros. Vamos falar mais disso conforme avançamos, mas no momento esses parâmetros representam:

  • component – o componente. Nesse caso, é helloMessageInteractive.
  • event – o evento que causou a chamada do manipulador de ação.
  • helper – o auxiliar do componente, outro recurso JavaScript de funções reutilizáveis.
    let btnClicked = event.getSource();         // the button

Lembre-se de que handleClick está conectado a nossa marca <lightning:button> e a seu atributo onclick. O event, então, é alguém clicar o botão. Dentro desse evento ele tem a noção de uma origem, aquilo que gerou o evento, que é o próprio botão. Então, chamar event.getSource() nos dá uma referência ao <lighning:button> específico que foi clicado.

    let btnMessage = btnClicked.get("v.label"); // the button's label

O que fazemos agora que temos uma referência ao botão? Nós olhamos dentro dele e pegamos seu rótulo, que está definido no <lightning:button> na marcação do componente. Por exemplo, <lightning:button label="Você está ótimo hoje." ... >.

Vamos pensar um pouco mais nisso. Não temos a definição de <lightning:button> na nossa frente, mas label é só mais um atributo, assim como o atributo message que adicionamos a helloMessageInteractive. Você pode chamar o get() em qualquer componente e fornecer o nome do atributo que deseja recuperar, no formato v.attributeName. O resultado é o valor do atributo.

Observe que, assim como na marcação de componente, v representa a visualização, o componente em si – mas nesse caso é o componente filho <lightning:button>, não helloMessageInteractive! Pense da seguinte forma: o btnClicked.get("v.label") bate no ombro de qualquer componente btnClicked e diz “Ei, me dá o v.label”. O componente pensa “v sou eu”, olha para dentro de si mesmo e dá o valor de seu atributo de rótulo.

Então, agora que temos uma sequência de caracteres de texto recebida do botão, só resta uma etapa: alterar nosso atributo message para o novo texto da mensagem. Obviamente, assim como get() o valor de um componente, set() escreve um valor.

    component.set("v.message", btnMessage);     // update our message

Porém, precisamos reparar em uma diferença importante. Nós chamamos o get() em btnClicked, o <lightning:button> que está dentro de helloMessageInteractive. Estamos chamando set() no component, o próprio componente helloMessageInteractive. Este é um padrão que vai se repetir em virtualmente todos os componentes criados: obtenha os valores de componentes filho, talvez efetue algum processamento, e defina os valores no próprio componente.

O modelo de programação do controlador de visualização dos componentes do Aura

OK, hora da verdade. Isto está fazendo sentido? Tem certeza? Se você acha que sim, é melhor garantir que você realmente criou o componente helloMessageInteractive usando o código anterior. É um componente, e copiar/colar o código leva dois minutos, mas ser capaz de brincar com ele é essencial para entender as ações de manipulação.

O que você fez aqui pode parecer muito simples, porque são poucas linhas de código. Mas essas linhas de código ilustram alguns dos conceitos fundamentais de criar aplicativos com componentes do Aura.

Pense em conectar os componentes a manipuladores de ação como se estivesse “ligando a fiação”. Pense em helloMessageInteractive como um circuito elétrico simples. Existem interruptores e lâmpadas. (A estrutura dos componentes do Lightning oferece a eletricidade.) Sozinho, um interruptor pode fazer um som de clique legal, mas até que a fiação seja ligada, ele não é lá muito funcional. Você pode ter a lâmpada estilo Edison mais chique do mundo, mas até a fiação ser ligada, ela não ilumina nada.

É a mesma coisa com os componentes do Aura. Lá atrás, falamos que os diferentes recursos em um pacote de componente são “auto conectados” entre si. E é verdade: a conexão tem a forma dos provedores de valor v e c. Eles são criados automaticamente e disponibilizados dentro de seus componentes, para que seu controlador possa fazer referência ao componente e vice-versa. Mas essa conexão automática só acontece em um nível alto – no helloMessageInteractive, entre o recurso .cmp do componente e o recurso .js do controlador. Isso é a seta verde na ilustração a seguir.

helloMessageInteractive e seu controlador têm conexão automática

A fiação para conectar um componente <lightning:button> específico a um manipulador de ações específico – ou seja, a conexão de coisas que geram eventos, como botões, a coisas que tratam eventos, como uma função do controlador específica – essa é uma conexão que você mesmo precisa fazer. Que, na verdade, você já fez sozinho! Elas são as setas vermelhas necessárias para concluir um circuito funcional.

Adicionar {!c.handleClick} ao atributo onclick de um componente <lightning:button> (1) conecta esse componente ao manipulador de ações específico. Chamar o component.set("v.message", newMessage) (2) conecta o resultado daquele manipulador de ação ao atributo message do componente. Que, por sua vez, está conectado à expressão {!v.message}.

Você também pode pensar no evento onclick como um elétron fluindo pelo circuito criado. Se você não criou um circuito completo, o evento não vai para lugar nenhum e nada acontece. Quando começar a escrever seus próprios componentes, tenha isso em mente. Você tem um circuito completo? Tem certeza? Se estiver em dúvida, por vezes ajuda fazer um esboço de tudo em um quadro branco ou no papel, e confirmar todas as conexões.

Você vai ligar componentes, eventos e manipuladores, em níveis altos e baixos, tantas vezes que vai se sentir como um eletricista. (Ou, considerando o nome da estrutura, talvez como Ben Franklin.) Conectar as coisas é fundamental para o modelo de programação dos componentes do Aura.

Então, vamos fazer um pouco mais disso. Afinal de contas, a prática leva à perfeição.

Encadeamento de função, novas conexões e depuração simples

Nossa primeira versão do handleClick tinha três linhas de código, porque separamos cada etapa no padrão obter-processar-definir em linhas separadas. É possível usar algo chamado encadeamento de função para que sejam ainda menos linhas. Já que você provavelmente vai ver isso em outro código dos componentes do Lightning, vamos dar uma olhada agora. Adicione o código adicional a seguir no seu controlador helloMessageInteractive, depois de handleClick.

    handleClick2: function(component, event, helper) {
        let newMessage = event.getSource().get("v.label");
        component.set("v.message", newMessage);
    },
    handleClick3: function(component, event, helper) {
        component.set("v.message", event.getSource().get("v.label"));
    }

Ooops! Você teve um erro de sintaxe ao tentar salvar? Lembra-se quando falamos antes que você precisaria adicionar vírgulas entre seus manipuladores de ação? Esse é o problema aqui. Adicione uma vírgula depois do colchete final (“}”) do handleClick, como pode ser visto no final do handleClick2 no trecho de código anterior.

Esses manipuladores de ação fazem exatamente a mesma coisa que o handleClick, em menos linhas de código. Eles fazem isso pulando as variáveis intermediárias, muitas vezes “encadeando” diretamente para a próxima chamada de função, ao adicionar aquela chamada ao final da chamada anterior, separada por um ponto. (Esse conceito fica mais claro ao olhar para as diferenças entre handleClick e handleClick2.)

Seu estilo preferido vai ser uma questão de gosto pessoal, e talvez do estilo de código da sua organização. Este humilde autor prefere handleClick2, separando get e set, mas sem se preocupar com uma variável para o botão, que só vamos precisar para seu texto de rótulo.

Claro, não é possível verificar se os novos manipuladores de ações funcionam até conectar um <lightning:button> para usar um deles, ao configurar o atributo onclick como {!c.handleClick2} ou {!c.handleClick3}. Pense nisso como se desconectasse os fios de uma lâmpada e os conectasse a uma lâmpada diferente. É fácil assim!

Nesse ponto você recarrega o aplicativo, clica em um dos botões conectados de novo e... bom, é o mesmo por design, não é? Como podemos saber qual manipulador de ação está sendo chamado?

Por vezes, o simples é o melhor. Vamos adicionar alguns registros a uma das funções do manipulador de ação:

    handleClick2: function(component, event, helper) {
        let newMessage = event.getSource().get("v.label");
        console.log("handleClick2: Message: " + newMessage);
        component.set("v.message", newMessage);
    },

Agora se você conectar um <lightning:button> ao handleClick2, verá uma mensagem de registro no console de JavaScript do seu navegador sempre que clicar nele.

Sim, existem ferramentas de depuração mais sofisticadas, mas imprimir coisas no console é uma técnica de depuração antiga e clássica. Se quiser criar um objeto de algum tipo, conclua com JSON.stringify(yourObject) para obter detalhes ainda mais úteis. Quando quiser só uma olhada rápida, console.log() é seu melhor amigo.

Não vamos falar aqui das ferramentas e técnicas de depuração mais sofisticadas, mas consulte os Recursos para ficar a conhecer algumas ótimas ferramentas e instruções.

OK, helloMessageInteractive foi ridiculamente simples e com uma atitude de código intensa (e sempre positiva). Na próxima unidade vamos trabalhar com algo mais complexo, e aprender como coletar entradas reais de usuários. E, como as pessoas no mundo real não são sempre tão positivas, vamos aprender também a validar suas entradas.