Inserir dados usando formulários

Objetivos de aprendizagem

Após concluir esta unidade, você estará apto a:
  • Criar um formulário para exibir os valores atuais e aceitar novas entradas de usuário.
  • Ler valores dos elementos de forma.
  • Validar entradas de usuário e exibir mensagens de erro para entradas inválidas.
  • Refatorar o código de um controlador do componente para seu auxiliar.

Inserir dados usando formulários

A partir dessa unidade, terminamos com os componentes no estilo helloWhatever. A partir daqui vamos criar e montar o mini aplicativo de rastreamento de despesas que visualizamos antes. Vamos passar a maior parte dessa unidade construindo e compreendendo a forma que lhe permite criar uma nova despesa.

O contêiner do aplicativo expenses

Mas antes de começarmos com isso, vamos tratar logo de criar componentes simples, ou mesmo feios. A primeira coisa a fazer, então, é abrir o Salesforce Lightning Design System, ou SLDS, e “ativá-lo” em nosso aplicativo. A forma como vamos fazer isso nos permite falar um pouco mais sobre contêineres de aplicativo.

Nota

Nota

Na verdade, não vamos discutir o SLDS em si nesta unidade, nem no resto do módulo. Nosso foco aqui é adicioná-lo a um aplicativo, e então, dentro de nosso código de exemplo, vamos usar as classes SLDS, mas não vamos explicar cada uma delas detalhadamente. Veja os Recursos para várias formas diferentes de aprender mais sobre o SLDS.

Hoje, o SLDS está disponível automaticamente para nossos componentes quando eles são executados dentro do Lightning Experience ou do aplicativo Salesforce. Chamamos isso, algumas vezes, de executar o contêiner de um .app. A versão embutida é a mesma usada por muitos dos componentes do Lightning padrão. Porém, o SLDS não está disponível por padrão em um aplicativo autônomo, ou quando você usar seus componentes no Lightning Out ou componentes do Lightning para Visualforce. Esses são contêineres de aplicativo diferentes, e oferecem serviços e recursos diferentes. Gostaríamos de criar nosso aplicativo de despesas de forma que ele funcione bem e tenha uma boa aparência em todos esses contextos. E, felizmente, isso não é tão difícil de fazer.

A forma como fazemos isso é adicionando SLDS em nosso aplicativo de aproveitamento. Então, dentro do aplicativo de despesas “real” (na verdade, o componente de maior nível e todos os seus componentes filho), podemos usar ferramentas e técnicas SLDS, sem nos preocuparmos sobre de onde os recursos SLDS – folhas de estilo, ícones e assim por diante – estão vindo. Isso quer dizer que nosso contêiner de aplicativo (o aplicativo de aproveitamento) define recursos dentro de seu contexto de forma que qualquer aplicativo sendo executado dentro daquele contêiner tem os recursos de que precisa.

Vamos converter esses longos conceitos em códigos. Crie um novo aplicativo Lightning expensesApp.app com a seguinte marcação.

<aura:application extends="force:slds">
        <!-- This component is the real "app" -->
        <!-- c:expenses/ -->
</aura:application>

O que está acontecendo é o seguinte. O atributo extends="force:slds" ativa o SLDS neste aplicativo, incluindo os mesmos estilos do Lightning Experience Design System fornecidos pelo Lightning Experience e pelo aplicativo Salesforce. Mas veja que esse aplicativo de aproveitamento é apenas uma cobertura, uma casca. O aplicativo real é o componente expenses, que nós não criamos. (É a parte <!-- c:expenses/ -->; não foi comentada porque não podemos salvar nosso aplicativo até que o componente expenses realmente exista.)

Através do aplicativo de cobertura, nossos componentes usam o mecanismo extends="force:slds" para obter acesso ao SLDS quando eles são executados de dentro deste aplicativo. Quando eles são executados dentro do Lightning Experience ou do aplicativo Salesforce, sem mudanças no código, eles usam a inclusão automática de SLDS daquele contêiner.

Nesse caso, o resultado é o mesmo. Mas esse conceito de usar o aplicativo de aproveitamento externo para definir um contexto para que o aplicativo real não precise se preocupar com diferenças de contexto não está limitado a recursos de estilo. Você pode usar isso para fornecer manipuladores de eventos de substituição, por exemplo... apesar de que isso é falar antes de tempo. Vamos aprender a andar antes de tentarmos voar!

O componente de aplicativo expenses

A próxima etapa é criar o componente que é o nível mais alto de nosso aplicativo de despesas. (Lembre-se, apesar de chamarmos de “aplicativo”, na verdade é só mais um componente do Lightning). No Developer Console, crie um novo componente do Aura chamado “expenses” e substitua a marcação padrão pelo seguinte.

<aura:component>
    <!-- PAGE HEADER -->
    <lightning:layout class="slds-page-header slds-page-header_object-home">
        <lightning:layoutItem>
            <lightning:icon iconName="standard:scan_card" alternativeText="My Expenses"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading_label">Expenses</h1>
                <h2 class="slds-text-heading_medium">My Expenses</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->
    <!-- NEW EXPENSE FORM -->
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="6">
        <!-- [[ expense form goes here ]] -->
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->
</aura:component>

O que estamos criando aqui é o cabeçalho da página que usa o layout de grade fornecido pelos componentes <lightning:layout> e <lightning:layoutItem>. size="6" cria um contêiner <div> que representa 50% da largura total (ou tamanho 6 de 12). Como você deve ter reparado, os componentes no namespace lightning se parecem com componentes no Lightning Experience e no aplicativo Salesforce. Além dos botões e layouts, você encontrará muitos outros componentes úteis nesse namespace que funcionam perfeitamente com o estilo SLDS automaticamente.

Nota

Nota

Reparou na marca <lightning:icon>? Esse componente processa seus ícones SLDS favoritos em um piscar de olhos. Criar um componente auxiliar para exibir ícones SLDS é coisa do passado.

Agora, você pode excluir os comentários na marca <c:expenses/> no próprio .app, e abrir a visualização do que é, no momento, nada mais do que uma casca vazia. Você deverá ver algo mais ou menos como o seguinte.

Formulário básico Minhas Despesas

Ainda não tem muita coisa acontecendo, mas é animador ver que a modelagem SLDS já está causando um efeito. Lembre-se que não vamos explicar a maioria das marcações SLDS, mas vamos incluir comentários na marcação. Você pode ver como criamos o cabeçalho do aplicativo e começar a entender a ideia.

O formulário de nova despesa

Antes de começarmos com o formulário, vamos já admitir algo sem rodeios: O que vamos fazer aqui é temporário. Lembra de toda aquela conversinha sobre decompor seu aplicativo em componentes menores separados, e depois construir em cima disso? Não vamos fazer isso aqui – ainda não – e para ser honesto é um pouco uma trapaça.

Mas é uma trapaça útil para impedir o código de ficar muito complicado, muito rápido. Estamos fazendo isso desse jeito para que possamos nos concentrar em uma lição de cada vez. Além do mais, não é um jeito ruim de você descobrir as coisas por si mesmo: construir dentro de um componente até que ele fique muito “ocupado” e depois refatorar e decompor em subcomponentes menores. Desde que você se lembre de refatorar novamente!

OK,  </preaching>. No componente expenses, substitua o comentário <!-- [[ expense form goes here ]] --> pelo código a seguir para o formulário Adicionar despesa.

    <!-- CREATE NEW EXPENSE -->
    <div aria-labelledby="newexpenseform">
        <!-- BOXED AREA -->
        <fieldset class="slds-box slds-theme_default slds-container_small">
        <legend id="newexpenseform" class="slds-text-heading_small
          slds-p-vertical_medium">
          Add Expense
        </legend>
        <!-- CREATE NEW EXPENSE FORM -->
        <form class="slds-form_stacked">
            <lightning:input aura:id="expenseform" label="Expense Name"
                             name="expensename"
                             value="{!v.newExpense.Name}"
                             required="true"/>
            <lightning:input type="number" aura:id="expenseform" label="Amount"
                             name="expenseamount"
                             min="0.1"
                             formatter="currency"
                             step="0.01"
                             value="{!v.newExpense.Amount__c}"
                             messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
            <lightning:input aura:id="expenseform" label="Client"
                             name="expenseclient"
                             value="{!v.newExpense.Client__c}"
                             placeholder="ABC Co."/>
            <lightning:input type="date" aura:id="expenseform" label="Expense Date"
                             name="expensedate"
                             value="{!v.newExpense.Date__c}"/>
            <lightning:input type="checkbox" aura:id="expenseform" label="Reimbursed?"
                             name="expreimbursed"
                             checked="{!v.newExpense.Reimbursed__c}"/>
            <lightning:button label="Create Expense"
                              class="slds-m-top_medium"
                              variant="brand"
                              onclick="{!c.clickCreate}"/>
        </form>
        <!-- / CREATE NEW EXPENSE FORM -->
      </fieldset>
      <!-- / BOXED AREA -->
    </div>
    <!-- / CREATE NEW EXPENSE -->

Eu sei que parece muito código para entrar na cabeça de uma vez. Não é. Quando você remove as classes e marcações SLDS, esse formulário se resume a uma série de campos de entrada e um botão para o envio de formulários.

Aqui está o formulário resultante.

Formulário de nova despesa
Nota

Nota

<lightning:input> é o canivete suíço para campos de entrada combinado com a beleza do estilo SLDS. Use-o sempre que estiver procurando a variedade de componente <ui:input>, como <ui:inputText>, <ui:inputNumber> e outros. Os componentes no namespace ui não vêm com estilo SLDS e são considerados componentes legados.

Primeiro, observe que estamos criando várias instâncias do componente <lightning:input> com tipos de dados específicos. Ou seja, não é nenhuma surpresa querer usar o type="date" com um campo de data, e assim por diante. Existe uma gama de tipos diferentes, bem além desses especificados, e é sempre melhor combinar o tipo de componente com o tipo de dados. Se você não especificar um tipo, ele será texto por padrão. O motivo pode ainda não ser óbvio, mas será quando você experimentar esse aplicativo em um telefone – componentes específicos do tipo podem fornecer widgets de entrada mais adequados ao fator de formulário. Por exemplo, o seletor de data é otimizado para mouse ou ponta do dedo, dependendo de onde for acessado.

A seguir, perceba que cada componente de entrada tem um rótulo definido e que o texto de rótulo é exibido automaticamente junto do campo de entrada. Existem alguns outros atributos que não vimos antes: required, placeholder, type, min e step. A maioria desses atributos é semelhante a seus equivalentes HTML. Por exemplo, min especifica o valor mínimo para a entrada. Se não conseguir adivinhar para o que eles servem, você pode procurar seu significado na biblioteca de componentes do Lightning. (E vamos voltar para aquele required enganador.)

Em seguida, há um atributo aura:id definido em cada marca. Para que ele serve? Ele define uma ID única (localmente) em cada marca à qual é adicionado, e essa ID é como você vai ler os valores dos campos de formulário. Todos os campos compartilham a mesma ID neste exemplo, para que possamos acessá-los como uma matriz na validação do campo. Vamos ver como fazer isso muito em breve.

Atributos para Salesforce Objects (sObjects)

Mas primeiro precisamos ver o atributo value. Cada marca tem um valor, definido para uma expressão. Por exemplo, {!v.newExpense.Amount__c}. Do formato da expressão, você deve conseguir deduzir algumas coisas.

  • v significa que é uma propriedade do provedor de valor de visualização. Isso significa que é um atributo no componente. (Que ainda não criamos.)
  • Com base na notação de ponto, é possível dizer que newExpense é como um tipo de dados estruturado. Ou seja, o próprio newExpense tem propriedades. Ou... campos?
  • A partir do “__c” que está no final da maioria dos nomes de propriedade, é possível adivinhar que eles são mapeados de volta para os campos personalizados, provavelmente no objeto personalizado Despesa.
  • Então newExpense provavelmente é um objeto de Despesa!

Legal, ainda não discutimos isso! Aqui está a definição de atributo real, que você deve adicionar ao topo do componente, logo depois da marca de abertura <aura:component>.

    <aura:attribute name="newExpense" type="Expense__c"
         default="{ 'sobjectType': 'Expense__c',
                        'Name': '',
                        'Amount__c': 0,
                        'Client__c': '',
                        'Date__c': '',
                        'Reimbursed__c': false }"/>

O que está acontecendo aqui na verdade é bastante simples. Você já conhece o atributo de nome. E o tipo, como seria de se esperar, é o nome da API de nosso objeto personalizado. Por enquanto, tudo bem.

O atributo padrão não é novo, mas o formato de seu valor é. Mas isso não deve ser difícil de entender. É uma representação JSON de um sObject, especificando o tipo de objeto (novamente, o nome da API) e os valores para cada um dos campos a definir nesse objeto por padrão. Nesse caso, vamos basicamente definir tudo para uma representação de um valor vazio.

E, bom, isso é quase tudo que você precisa saber sobre os sObjects! A partir daqui a estrutura dos componentes do Lightning deixará você tratar o objeto newExpense, no JavaScript e na marcação, como um registro do Salesforce – mesmo que não estejamos (ainda) carregando o registro a partir do Salesforce!

Tratar de envios de formulário em um manipulador de ação

Temos aqui um formulário. Nesse momento, se você o preencher e clicar no botão para criar uma nova despesa, o que acontece? A menos que você tenha se adiantado e já tenha criado a despesa, você vai receber outro erro sobre uma ação de controlador que está faltando. Isso acontece porque nem o controlador nem o manipulador de ações especificado no <lightning:button> foram criados.

No Console do desenvolvedor, clique no botão CONTROLADOR do componente expenses para criar o recurso do controlador. Então, substitua o código padrão pelo seguinte.

({
    clickCreate: function(component, event, helper) {
        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);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            let newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})

OK, isso tudo é novo, então vamos olhar com cuidado. Primeiro, vamos notar que esta função do manipulador de ação está basicamente dividida em três seções, ou etapas:

  1. Configuração
  2. Processar valores de formulário
  3. Se não existirem erros, fazer alguma coisa

Você pode estar familiarizado com essa estrutura, pois é uma forma bastante fundamental de processar entradas de usuário em um aplicativo da web. Vamos ver cada uma das etapas, para saber como elas funcionam no componente do Lightning.

Para a configuração, só precisamos inicializar o estado de nossa verificação de erros. É um indicador simples: essa despesa é válida? Cada vez que o manipulador de ações clickCreate é chamado, vamos partir da suposição de que os dados da despesa estão OK, e invalidar os dados se acharmos um problema. Eis um resumo do indicador validExpense, com valor inicial definido como true.

  • component.find('expenseform') recebe uma referência à matriz dos campos <lightning:input> que exigem validação. Se a ID local for exclusiva, a referência retorna o componente. Neste caso, a ID não é exclusiva e a referência retorna uma matriz de componentes.
  • O método JavaScript reduce() reduz a matriz a um único valor que é capturado por validSoFar, que permanecerá true até encontrar um campo inválido e, consequentemente, alterará validSoFar para false. Um campo inválido pode ser um campo obrigatório que está vazio, um campo que tenha um número abaixo do mínimo especificado, dentre outros.
  • inputCmp.get('v.validity').valid retorna a validade do campo de entrada atual na matriz.
  • inputCmp.showHelpMessageIfInvalid() exibe mensagens de erro para campos inválidos. <lightning:input> fornece mensagens de erro padrão que podem ser personalizadas por atributos como messageWhenRangeUnderflow, que você viu no exemplo de formulário de despesas.

Vamos ver alguns detalhes interessantes. Voltando para o helloMessageInteractive, nós não usamos find() para descobrir o texto de rótulo do botão que foi clicado. É porque não precisamos. Nós conseguimos obter uma referência para aquele botão diretamente, retirando-a do parâmetro event usando event.getSource(). Não é sempre que você vai ter esse luxo. Na verdade, é raro ter tudo o que você precisa da entrada do usuário apenas no evento.

Então, quando seu controlador precisar de uma forma para obter um componente filho, primeiro, estabeleça um atributo aura:id naquele componente na marcação, e depois use component.find(theId) para obter uma referência ao componente no tempo de execução.

Nota

Nota

component.find () só permite acessar o componente e seus componentes filho do controlador e do auxiliar. Ele não é um jeito mágico de passear pela hierarquia de componentes e ler ou alterar coisas. Lembre-se, os componentes devem ser autossuficientes, ou precisam se comunicar com... bom, vamos chegar lá.

A validação com <lightning:input> utiliza o poder do elemento de entrada HTML subjacente para processar valores de formulário, evitando que você tenha que fazê-lo na maioria dos casos. Precisa validar um número de telefone? Use type="tel" e defina o atributo pattern usando uma expressão regular. Precisa validar um valor de porcentagem? Use type="number" com formatter="percent". Precisa validar mais alguma coisa? Deixe <lightning:input> fazer o trabalho pesado para você.

Quando a validação falha é que as coisas ficam interessantes de novo. Quando o usuário digita uma entrada inválida, queremos que duas coisas aconteçam:

  1. Não tentar criar a despesa.
  2. Exibir uma mensagem de erro útil.

No primeiro caso, definimos o indicador validExpense como false quando a validade do campo referenciado por inputCmp.get('v.validity').valid for avaliada como false. No segundo caso, usamos os erros de validação integrados ou fornecemos mensagens personalizadas para os erros. No formulário de despesas, o campo de nome obrigatório exibirá “Preencher este campo” se o campo estiver vazio e você tentar enviar o formulário. Mas você pode oferecer sua própria mensagem personalizada especificando messageWhenValueMissing="Esqueceu-se de mim?"

Por contraste, se o campo for validado, o indicador validExpense será avaliado como true e nenhum erro será exibido.

E com isso, estamos na etapa três de como lidar com o envio de formulário: criar a despesa na prática! Como você pode ver, para nos preparar para isso estamos pegando o objeto newExpense completo do atributo de componente: component.get("v.newExpense"). Isso nos dá uma única variável que podemos usar para criar um novo registro de despesa.

Mas, antes de começarmos com isso, deixamos uma questão para você pensar: por que não obtemos os valores do formulário de newExpense? Obter a variável estruturada uma vez, no início do manipulador de ação, e depois simplesmente acessar suas propriedades, em vez de fazer o que pode ser uma longa série de chamadas find().get()?

O motivo é simples: porque precisamos de referências aos campos individuais para conseguirmos definir os showHelpMessageIfInvalid() neles. Também é uma boa prática validar os dados de formulário brutos; sua lógica de validação não sabe quais tipos de processamento podem acontecer dentro do objeto newExpense.

Criar a nova despesa

Lembra quando falamos anteriormente que colocar o formulário de despesa no componente principal era um pouco de trapaça? Bom, nessa próxima seção esqueça o “um pouco”. O que estamos fazendo aqui é simplesmente evitar a complexidade de realmente criar um registro. E estamos evitando isso agora porque essa é a próxima unidade inteira. Então, vamos terminar essa com algo simples, mas que ainda mostra alguns conceitos importantes.

Primeiro, vamos criar um lugar para “armazenar” novas despesas. Vamos simplesmente criar uma matriz apenas local de despesas para armazená-las. No topo da marcação do componente expenses, logo antes do atributo newExpense, adicione um novo atributo expenses que vai reter uma matriz de objetos de despesa.

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

Tudo que precisamos fazer é atualizar a matriz expenses. O que, no fim de contas, é fácil (nessa versão ligeiramente trapaceira do nosso formulário) e ilustra um outro conceito importante.

Em nosso controlador, ocultamos o trabalho de realmente criar a nova despesa por trás desta chamada de função: helper.createExpense(component, newExpense). No desenvolvimento de software, outra palavra para “ocultar” é abstrair. E estamos usando algo chamado de auxiliar para abstrair nossa pequena trapaça.

Já falamos um pouco dos auxiliares antes, e não vamos tratar dos detalhes avançados dos auxiliares nesse módulo. Por enquanto, falaremos três coisas sobre os auxiliares:

  • O auxiliar de um componente é o local adequado para colocar um código a ser compartilhado entre vários manipuladores de ação diferentes.
  • O auxiliar de um componente é um ótimo local para colocar detalhes de processamento complexos, para que a lógica dos seus manipuladores de ação continue clara e sem interrupções.
  • As funções do auxiliar podem ter qualquer assinatura de função. Ou seja, elas não são restritas como são os manipuladores de ação no controlador. (Por que isso? Porque você está chamando a função do auxiliar diretamente do seu código. Por contraste, a estrutura chama manipuladores de ação através do tempo de execução da estrutura.) É uma convenção e uma prática recomendada sempre fornecer o componente como o primeiro parâmetro para as funções do auxiliar.

OK, vamos continuar com isso. No Console do desenvolvedor, clique no botão AUXILIAR para o componente expenses criar o recurso auxiliar associado e, em seguida, substitua o código de exemplo com o que se segue.

({
    createExpense: function(component, expense) {
        let theExpenses = component.get("v.expenses");
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        let newExpense = JSON.parse(JSON.stringify(expense));
        theExpenses.push(newExpense);
        component.set("v.expenses", theExpenses);
    }
})

No momento, ignore a parte horrível de hack. As outras três linhas de código ilustram um padrão comum que vimos antes, e que você usará repetidamente: obter-processar-definir. Primeiro, pegamos a matriz de despesas do atributo expenses. Depois, adicionamos a ele o novo “registro” de despesa. Depois, atualizamos (set) o atributo expenses com a matriz modificada.

A referência não é a coleção

A novidade aqui é que, pela primeira vez, estamos atualizando uma coleção, uma matriz. E, se você for um programador experiente, provavelmente está se perguntando: “Para que eu preciso do set() aqui?”

Ou seja, component.get ("v.expenses") obtém uma referência à matriz armazenada no atributo component. component.set ("v.expenses", theExpenses) simplesmente define o atributo component para a mesma referência. Claro, entre eles foi adicionado o conteúdo da matriz, mas o contêiner é o mesmo: a referência para a matriz não mudou! Então, por que atualizar?

Se você está tendo problemas para entender o que isso significa, adicione dois demonstrativos de registros antes e depois dos demonstrativos críticos, e jogue o conteúdo de theExpenses no console.

console.log("Expenses before 'create': " + JSON.stringify(theExpenses));
theExpenses.push(newExpense);
component.set("v.expenses", theExpenses);
console.log("Expenses after 'create': " + JSON.stringify(theExpenses));

Recarregue e execute o aplicativo, adicione pelo menos duas despesas e olhe a estrutura de theExpenses. Agora faça um comentário na linha component.set() e repita o processo.

Mas...? component.set() não afetou em nada theExpenses! Mas! Mas! Mas? O que ele realmente faz?!?

Você está absolutamente certo de estar fazendo essa pergunta. E a resposta é: mágica!

O que o component.set() faz aqui não é atualizar o valor do atributo expenses. Ele aciona a notificação de que o atributo expenses foi alterado.

O efeito é que, em qualquer lugar do aplicativo que faça referência ao atributo expenses em uma expressão, o valor da expressão é atualizado e a atualização é enviada em cascata para todos os lugares onde o atributo expenses foi usado. E todos eles atualizam para renderizar novamente com base no novo conteúdo. Isso tudo acontece nos bastidores, tratado pela estrutura dos componentes do Lightning, como parte da conexão automática que acontece quando você usa o {!v.expenses}. Em uma palavra, mágica.

Resumindo, se isso fosse JavaScript simples você não precisaria do component.set(). Mas para acionar os efeitos subjacentes embutidos no modelo de programação de componentes do Aura, você precisa. Se algum dia você escrever um código de controlador ou auxiliar, testar e nada acontecer, certifique-se de ter feito o component.set() necessário.

O “hack horrível” contorna um problema semelhante relacionado a referências. Para ver o problema, altere a linha para remover as duas chamadas JSON e teste o aplicativo. Você verá qual é o problema rapidinho. Vamos removê-lo na próxima unidade, então não vamos explicar mais.

Exibir a lista de despesas

Então, depois de todo aquele papo sobre atualizar “magicamente” qualquer coisa que use o {!v.expenses}, adivinhe. Ainda não tem mais nada usando isso. Vamos mudar isso.

No Developer Console, crie um novo componente do Aura chamado expenseItem e substitua a marcação padrão pelo seguinte. Se você já criou o expenseItem, é só atualizar a marcação. Você já viu as expressões que acessam campos no registro de despesa. Esta versão inclui marcação SLDS para torná-lo mais estiloso.
<aura:component>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="formatdate" type="Date"/>
    <aura:attribute name="expense" type="Expense__c"/>
    <lightning:card title="{!v.expense.Name}" iconName="standard:scan_card"
                    class="{!v.expense.Reimbursed__c ?
                           'slds-theme_success' : ''}">
        <aura:set attribute="footer">
            <p>Date: <lightning:formattedDateTime value="{!v.formatdate}"/></p>
            <p class="slds-text-title"><lightning:relativeDateTime value="{!v.formatdate}"/></p>
        </aura:set>
        <p class="slds-text-heading_medium slds-p-horizontal_small">
           Amount: <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
        </p>
        <p class="slds-p-horizontal_small">
            Client: {!v.expense.Client__c}
        </p>
        <p>
            <lightning:input type="toggle"
                             label="Reimbursed?"
                             name="reimbursed"
                             class="slds-p-around_small"
                             checked="{!v.expense.Reimbursed__c}"
                             messageToggleActive="Yes"
                             messageToggleInactive="No"
                             onchange="{!c.clickReimbursed}"/>
        </p>
    </lightning:card>
</aura:component>
Reparou que <lightning:card> atribui um tema SLDS quando o campo Reembolsado? do item de despesa está marcado: {!v.expense.Reimbursed__c ? 'slds-theme_success' : ''}. Essa expressão fornece controle sobre a aparência dos itens de despesa reembolsados na interface do usuário. Sem qualquer personalização adicional, os itens de despesa aparecem no estilo do componente expensesList que contém os itens de despesa. Para personalizar o componente expenseItem, clique em ESTILO e adicione o item a seguir.
.THIS.slds-card.slds-theme_success {
    background-color: rgb(75, 202, 129);
}

Em seguida, crie um controlador do lado do cliente expenseItemController.js com o código a seguir. Aqui, estamos convertendo a data que é retornada pelo servidor posteriormente para um objeto de data JavaScript, para que seja exibido corretamente por <lightning:formattedDateTime> e <lightning:relativeDateTime>. Essa conversão é tratada durante a inicialização do componente, capturada pela marca <aura:handler>. Essa é uma maneira útil de lidar com o evento de inicialização e a usaremos novamente mais tarde ao carregar dados do Salesforce.

({
    doInit : function(component, event, helper) {
        let mydate = component.get("v.expense.Date__c");
        if(mydate){
            component.set("v.formatdate", new Date(mydate));
        }
    },
})
No Developer Console, crie um novo componente do Aura chamado expensesList e substitua a marcação padrão pelo seguinte.
<aura:component>
    <aura:attribute name="expenses" type="Expense__c[]"/>
    <lightning:card title="Expenses">
        <p class="slds-p-horizontal_small">
            <aura:iteration items="{!v.expenses}" var="expense">
                <c:expenseItem expense="{!expense}"/>
            </aura:iteration>
        </p>
    </lightning:card>
</aura:component>

Não há praticamente nada de novo aqui. Este é um componente que exibe uma lista de despesas. Ele tem um atributo, expenses, que é uma matriz de objetos expense (Expense__c). E ele usa um <aura:iteration> para criar um <c:expenseItem> para cada um desses objetos de despesa. O efeito geral, como nós observamos, é a exibição de uma lista de despesas. Por enquanto, tudo bem.

Agora, adicione o componente expensesList no final do componente expenses. Adicione logo antes da marca final </aura:component> em expenses.cmp.

<c:expensesList expenses="{!v.expenses}"/>

Se você recarregar o aplicativo, verá uma seção de Despesas abaixo do formulário. (Visualmente ainda não está a cem por cento, mas está bom o bastante para o momento.)

O que acabamos de fazer? Adicionamos o componente expensesList a e passamos o atributo principal expenses para dentro dele. Então, agora, a instância de expenses que expensesList tem é a mesma instância de expenses que o componente expenses tem. Elas são referências para a mesma matriz de registros, e através da mágica dos componentes do Lightning, quando a matriz expenses principal é atualizada, o componente expensesList vai “perceber” e renderizar novamente sua lista. Experimente!

Uau! Essa foi uma unidade longa, e ela pede uma pausa. Por favor, levante e vá dar uma andada por alguns minutos.

Depois, volte e vamos mostrar como salvar realmente suas novas despesas.