Atributos e Expressões

Objetivos de aprendizagem

Após concluir esta unidade, você estará apto a:
  • Definir atributos em seus componentes e transmitir valores de atributos em componentes aninhados.
  • Entender a diferença entre uma definição de componente e uma instância do componente e criar várias instâncias de um componente.
  • Criar expressões básicas para exibir valores que mudam e valores calculados.
  • Criar expressões condicionais para saída dinâmica.

Atributos dos componentes

Até este ponto, embora tenhamos criado alguns componentes, e aprendido algo sobre a criação de aplicativos com esses componentes (em um nível elevado), o código que escrevemos não vai muito além daquilo que um simples HTML faz. Ou seja, os dois componentes criados geram o mesmo texto estático, não importa o que fizermos. Você poderia colocar vários deles na mesma tela que eles diriam sempre a mesma coisa.

Que chato.

Para mudarmos isso, precisamos de aprender duas coisas. Primeiro, precisamos saber como ativar um componente para aceitar a entrada quando ela for criada. Ou seja, é preciso definir valores no componente. Isso é feito usando atributos.

(A segunda coisa que precisamos é saber como realmente usar esses valores para alterar o resultado e o comportamento de um componente. Vamos tratar disso depois de entendermos os atributos.)

Atributos em componentes são como variáveis de instância em objetos. São uma maneira de salvar valores que mudam e uma forma de nomear esses espaços reservados de valor. Por exemplo, vamos supor que quiséssemos gravar um componente helloMessage para imprimir uma mensagem personalizada. Vamos imaginar um atributo de mensagem sendo adicionado ao componente para personalizar sua saída. E, em seguida, podemos definir essa mensagem ao adicionar o componente ao nosso aplicativo, da seguinte forma.
<aura:component>
    <c:helloMessage message="You look nice today."/>
</aura:component>
Não se esqueça de adicionar esse componente à sua organização, pois vamos usá-lo mais algumas vezes à medida que avançarmos. Mas se fizer isso agora, você verá um erro. Por quê? Porque o componente helloMessage ainda não existe. O Salesforce valida seu código conforme você o escreve. Se você tentar salvar código que ele reconhece como inválido, por exemplo, referenciando um componente não existente, verá um erro. Sendo assim, vejamos como criar um helloMessage primeiro.

Você pode definir os atributos de um componente ao criá-lo, como fizemos no exemplo anterior. Também é possível alterá-los ao longo do ciclo de vida do componente em resposta às ações realizadas pelo usuário ou aos eventos que acontecem em outros lugares e assim por diante. E, claro, você pode ler e usar valores de atributos de várias maneiras diferentes. Isso será analisado quando chegarmos às expressões.

Por enquanto, vamos ver como definir atributos para um componente. Um atributo é definido usando uma marca <aura:attribute>, que requer valores para os atributos name e type, e aceita os atributos opcionais: default, description, required.

Uau, há muitas formas diferentes de usar o “atributo” em uma frase! É muito fácil se confundir aqui porque temos três conceitos diferentes com nomes semelhantes. Vamos ser específicos.

  1. O atributo de um componente é o lugar onde podemos armazenar um valor. No exemplo anterior, o componente helloMessage possui um atributo de componente chamado message. Na maioria das vezes, estamos falando de atributos dos componentes.
  2. O atributo de um componente é definido usando a marca <aura:attribute>. Vamos ver um exemplo disso em breve. Vamos chamar de definições de atributos.
  3. A própria marca <aura:attribute> assume atributos quando é usada! Ou seja, ao definir um atributo do componente usando a marca <aura:attribute>, você configura atributos em <aura:attribute> que especificam a “forma” do atributo do componente que você está definindo. Espere, vamos tentar de novo: adicione uma definição de atributo do componente configurando atributos na definição do atributo. A definição de atributo de um atributo do componente assume atributos?

É esse o motivo por que escritores . Vamos tentar resolver este problema de terminologia com alguns códigos.

Veja o início do componente helloMessage:

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Hello! [ message goes here, soon ]</p>
</aura:component>

O componente helloMessage possui um atributo do componente e esse atributo é definido ao configurar o name e o type do atributo. O nome do atributo é message e, assim que aprendermos sobre expressões, essa será a forma como você fará referência ao atributo. Ele ainda só envia texto estático e HTML, mas estamos avançando cada vez mais para algo útil.

?

O outro atributo que usamos aqui foi type e ele foi definido porque isso é exigido na definição de um atributo. Ela diz que o atributo message contém uma sequência de caracteres que faz sentido. Discutiremos mais sobre tipos de dados de atributo e outras partes das definições de atributos, mas, primeiro, vamos dar uma olhada nas expressões e fazer com que helloMessage realmente faça alguma coisa.

Expressões

Em vez de nos perdermos nas palavras novamente, vamos nos aprofundar diretamente em fazer com que helloMessage funcione como pretendido.

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Hello! {!v.message}</p>
</aura:component>

Decepcionante, não é?

Estamos gerando os conteúdos de message usando a expressão {!v.message}. Ou seja, essa expressão faz referência ao atributo message. A expressão é avaliada e é resolvida na sequência de caracteres de texto que, no momento, está armazenada em message. E é isso que a expressão gera no corpo do componente.

Ummm…mas que raios é uma “expressão”?

Uma expressão é basicamente uma fórmula ou um cálculo que você coloca entre delimitadores de expressão (“{!” e “}”). Portanto, as expressões têm essa aparência:

{!<expression>}

A definição formal de uma expressão intimida um pouco, mas vamos observar e, em seguida, desempacotá-la: Uma expressão é qualquer conjunto de valores literais, variáveis, subexpressões ou operadores que podem ser resolvidos a um único valor.

Sim, basicamente uma fórmula, assim como você escreveria em um campo de cálculo, em critérios de filtro ou no Visualforce. A fórmula, ou expressão, pode conter várias coisas. Os valores literais devem ser óbvios, coisas como o número 42 ou a string “Hello”. As variáveis são coisas como o atributo message. Os operadores são coisas como +, - e assim por diante, e as subexpressões basicamente significam que você pode usar parênteses para agrupar itens.

Vamos experimentar isso tornando nossa expressão um pouquinho mais complexa.

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>{!'Hello! ' + v.message}</p>
</aura:component>

Tudo o que fizemos foi mover a parte do “Hello” do texto estático fora da expressão para texto literal dentro da expressão. Observe que usamos o operador “+” para concatenar as duas sequências de caracteres juntas. Pode parecer uma diferença pequena, mas mover o texto de saudação para dentro da expressão permite que você use rótulos, em vez de texto literal, e isso facilita a atualização (e tradução) de seus componentes. Por exemplo:

{!$Label.c.Greeting + v.message}

Você notou o que nossa definição formal de expressões deixou de fora? As chamadas de função do JavaScript. O JavaScript não pode ser usado em expressões na marcação dos componentes do Aura.

Uma última coisa sobre expressões antes de prosseguirmos. Você pode transmiti-las para outro componente a fim de definir o valor nele. Vejamos um novo componente que transmite um valor personalizado ao componente helloMessage. A transmissão do valor para o outro componente substitui o valor nesse componente.
<aura:component>
    <aura:attribute name="customMessage" type="String"/>
    <p> <c:helloMessage message="{!v.customMessage}"/> </p>
</aura:component>
.

Value Providers

Na verdade, precisamos falar sobre outro aspecto das expressões. Nos exemplos anteriores, usamos v.message para fazer referência ao atributo da mensagem do componente helloMessage. O que significa a parte “v.”?

v é algo chamado de provedor de valor. Os provedores de valor são uma forma de agrupar, encapsular e acessar dados relacionados. Provedores de valor são um tópico complicado, portanto, por enquanto, pense no v como uma variável automática que fica disponível para você usar. No nosso componente, v é um provedor de valor do view, que é o próprio componente helloMessage.

v oferece um “gancho” para acessar o atributo message do componente e é dessa forma que você acessa todos os atributos de um componente.

Valores em um provedor de valor são acessados como propriedades nomeadas. Para usar um valor, separe o provedor de valor e o nome da propriedade com um ponto. Por exemplo, v.message, como já vimos.

Quando um atributo de um componente é um objeto ou outros dados estruturados (ou seja, não um valor primitivo), acesse os valores nesse atributo usando a mesma notação de ponto. Por exemplo, {!v.account.Id} acessa o campo Id de um registro de contas. Para objetos e atributos profundamente aninhados, continue adicionando pontos para atravessar a estrutura e acessar os valores aninhados.

Tipos de dados de atributo

O acesso a dados estruturados é uma boa forma de mudarmos de assunto para falar sobre atributos e, especificamente, sobre tipos de atributo não primitivos. message é uma sequência de caracteres, mas há vários tipos de atributo diferentes.

  • Tipos de dados de números primitivos, como booleanos, data, data/hora, decimais, duplos, números inteiros, longos ou sequência de caracteres. Os suspeitos habituais em qualquer linguagem de programação.
  • Objetos padrão e personalizados do Salesforce, como Conta ou MyCustomObject__c.
  • Coleções, como List, Map e Set.
  • Classes do Apex personalizadas.
  • Tipos específicos da estrutura, como Aura.Component ou Aura.Component[]. Esses vão bem mais além daquilo que vamos abordar neste módulo, mas é bom você saber que eles existem.

Esta é a forma simplificada do componente expenseItem que será posteriormente preenchido. Ela ilustra como definir um atributo para um objeto personalizado e como acessar campos em um registro.

<aura:component>
    <aura:attribute name="expense" type="Expense__c"/>
    <p>Amount:
        <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
    </p>
    <p>
        Client: {!v.expense.Client__c}
    </p>
    <p>
        <lightning:input type="toggle"
                         label="Reimbursed?"
                         name="reimbursed"
                         checked="{!v.expense.Reimbursed__c}" />
     </p>
    <!-- Other markup here -->
</aura:component>

Este componente tem um atributo, expense, que é o objeto personalizado que criamos lá no início desse módulo. O objetivo do componente é exibir os detalhes de uma despesa referenciando o campo no registro Expense__c, usando a expressão {!v.expense.fieldName}. Estamos usando o componente <lightning:input> de type="toggle", que é uma caixa de seleção com formato de botão de alternância, para que possamos atualizar o valor na interface de usuário mais tarde.

Outros aspectos das definições de atributos

Em relação aos atributos definidos na marca <aura:attribute>, está aqui o restante daquilo que você precisa saber.

  • O atributo default define o valor do atributo padrão. Ele é usado quando o atributo é referenciado e você ainda não definiu o valor do atributo.
  • O atributo required define se o atributo é obrigatório. O padrão é false.
  • O atributo description define um breve resumo do atributo e de seu uso.

Definir o valor padrão para um atributo com um tipo de dados complexo pode ser um pouco complicado. Veremos um exemplo mais tarde, porém, por enquanto vamos apenas dar-lhe uma perspectiva.

Diversão com atributos e expressões

Para ilustrar mais alguns conceitos sobre os atributos e expressões, vamos criar um componente muito simples, helloPlayground, com a seguinte marcação.

<aura:component>
    <aura:attribute name="messages" type="List"
        default="['You look nice today.',
            'Great weather we\'re having.',
            'How are you?']"/>
    <h1>Hello Playground</h1>
    <p>Silly fun with attributes and expressions.</p>
    <h2>List Items</h2>
    <p><c:helloMessage message="{!v.messages[0]}"/></p>
    <p><c:helloMessage message="{!v.messages[1]}"/></p>
    <p><c:helloMessage message="{!v.messages[2]}"/></p>
    <h2>List Iteration</h2>
    <aura:iteration items="{!v.messages}" var="msg">
        <p><c:helloMessage message="{!msg}"/></p>
    </aura:iteration>
    <h2>Conditional Expressions and Global Value Providers</h2>
    <aura:if isTrue="{!$Browser.isIPhone}">
        <p><c:helloMessage message="{!v.messages[0]}"/></p>
    <aura:set attribute="else">
        <p><c:helloMessage message="{!v.messages[1]}"/></p>
        </aura:set>
    </aura:if>
</aura:component>

Agora, adicione o componente helloPlayground ao seu aplicativo agente e veja como ele funciona!

Há muitas coisas novas aqui. Não vamos nos aprofundar nas novidades, mas você verá tudo isso novamente.

Primeiro, helloPlayground tem um atributo, messages, que é um tipo de dados complexo, Lista. Além disso, possui um valor padrão para a lista, uma matriz de três sequências de caracteres entre aspas simples separadas por vírgulas. E, na seção Itens da lista, é possível ver como acessar cada uma das sequências de caracteres usando um índice.

O que acontece se alguém criar um <c:helloPlayground> com apenas duas mensagens? O acesso ao terceiro item não funcionará e não causará uma falha aqui, mas, com componentes mais complexos, pode ser que cause.

Portanto, na seção Iteração da lista, você pode encontrar uma forma melhor de trabalhar com todos os itens na lista. O componente <aura:iteration> repete o corpo uma vez por item no atributo items. Dessa forma, a lista diminui ou aumenta conforme temos mais ou menos mensagens.

Na seção Expressões condicionais e Provedores de valores globais, é possível encontrar uma maneira de escolher entre duas saídas possíveis diferentes. O formato é um pouco estranho por ser uma marcação em vez de, vamos supor, JavaScript, mas o componente <aura:if> permite, por exemplo, adicionar um botão de edição a uma página apenas se o usuário tiver privilégios de edição no objeto.

Por fim, algo um pouco menos óbvio. Na programação voltada para objetos, existe uma diferença entre uma classe e uma instância dessa classe. Os componentes têm um conceito semelhante. Ao criar um recurso .cmp, você está fornecendo a definição (classe) do componente. Ao colocar uma marca de componente em um .cmp, você está criando uma referência (instância) àquele componente.

Não é de se espantar que podemos adicionar várias instâncias do mesmo componente com atributos diferentes. No exemplo anterior, ao usar o valor padrão para mensagens, você acabará com oito referências para (instâncias do) nosso componente <c:helloMessage>. Se passar em uma lista maior, poderá ficar com (muitos) mais. Tudo com nosso componentezinho!

E com ele sabemos que você está preparado para o trabalho de verdade.