Alura > Cursos de Front-end > Cursos de JavaScript > Conteúdos de JavaScript > Primeiras aulas do curso JavaScript: evoluindo a sua aplicação com ES6+

JavaScript: evoluindo a sua aplicação com ES6+

Implementando um campo de busca - Apresentação

Boas-vindas a mais este curso de JavaScript! Me chamo Nayanne Batista e serei a sua instrutora.

Audiodescrição: Nayanne se declara como uma mulher de pele morena, com olhos e cabelos castanhos escuros, e está vestindo uma camisa laranja. Ao fundo, há uma estante com livros e decorações, além de uma janela à esquerda com iluminação verde.

Aplicação utilizada neste curso

Evoluiremos o projeto Memoteca, um mural de pensamentos que conta com um formulário intitulado "Adicione um pensamento novo" para adicionar conteúdo, autoria e data, além de um mural com os pensamentos preferidos cadastrados na parte inferior, denominado "Meu Mural".

Nesta aplicação, implementaremos o campo de data no formulário, permitindo que você aprenda a manipular datas em JavaScript e a personalizá-las para apresentá-las de forma agradável à pessoa usuária. Além disso, integraremos uma busca typeahead ("digitação antecipada"), que sugere resultados dinamicamente conforme o texto é digitado.

Implementaremos também a funcionalidade de favoritar pensamentos clicando no ícone de coração na parte inferior direita de um dos pensamentos do "Meu Mural", além de aplicar validações no formulário com o uso de regex para evitar a adição de pensamentos duplicados.

O que vamos aprender para implementar essas funcionalidades?

Utilizaremos diversos métodos do ES6+ (ECMAScript 6 e versões posteriores), como o replaceAll, o spreadOperator e a desestruturação, além de outros métodos e recursos do JavaScript.

Pré-requisitos

Os pré-requisitos para este curso incluem conhecimentos em HTML, CSS e JavaScript, especialmente sobre funções e requisições HTTP. É recomendável também que você tenha concluído os cursos anteriores da formação.

Estou muito animada para começar, e espero que você também esteja empolgado. Vamos lá!

Implementando um campo de busca - Criando o layout de um campo de busca

Chegou o momento de aprimorar a aplicação Memoteca. Neste curso, adicionaremos diversas funcionalidades para tornar nosso projeto mais completo e robusto, além de melhorar a experiência da pessoa usuária.

Contextualizando o problema

O projeto é o "Mural de Pensamentos". Com o crescimento da aplicação e o cadastro de novos pensamentos, torna-se mais difícil encontrar um pensamento específico.

Imagine um e-commerce com milhares de produtos. Mesmo utilizando filtros por categoria, pode ser difícil localizar um produto específico entre tantos outros.

Solução

Para resolver esse problema, seria ideal implementar uma funcionalidade que facilite a busca e a localização desses produtos.

Vamos ao Figma!

Interface de usuário com cabeçalho intitulado 'Meu Mural' e abaixo um campo de busca com o texto 'O que você procura?'.

Abaixo do título "Meu Mural", há um campo de busca. A ideia é permitir que a pessoa digite uma palavra-chave ou termo e encontre o que procura. Neste vídeo, vamos começar a implementar essa funcionalidade, dividindo o processo em várias etapas.

Etapas que seguiremos:

A primeira etapa é a parte da interface. Começamos adicionando o campo de busca no mural. Em seguida, avançamos para o envio das palavras-chave para a API e o retorno dos pensamentos necessários.

Adicionando o campo de busca no mural

Acessamos a aplicação e abrimos o VSCode. Com o comando "Ctrl + J" para abrir o terminal, verificamos que o back-end já está em execução, pois digitamos npm start.

npm start

Terminal da instrutora:

Endpoints:

http://localhost:3000/pensamentos

No arquivo index.html, adicionamos um novo bloco para representar o campo de busca. Observamos que já temos uma section na linha 30, que representa o container do formulário (<section id="form-container">), e outra section na linha 56, que representa o container da lista de pensamentos (<section id="lista-pensamentos-container">).

Adicionaremos uma nova section para representar o container do campo de busca.

É sempre importante utilizar HTML o mais semântico possível. Em vez de adicionar várias divs, optamos por usar tags que representem melhor o que estamos fazendo no código. Abaixo do h3, na linha 58, criamos uma nova section. Para identificar claramente essa seção, adicionamos um id com o valor busca-container.

index.html

<!-- código omitido -->

<section id="busca-container">
    
</section>

<!-- código omitido -->

Dentro da nova section, incluímos um input para permitir que a pessoa digite. Utilizamos o <input type="text">, quebramos para facilitar a visualização e definimos o id como campo-busca, que poderá ser usado posteriormente para estilizar. Adicionamos também um placeholder com a mensagem "O que você procura?".

<!-- código omitido -->

<section id="busca-container">
    <input 
        type="text"
        id="campo-busca"
        placeholder="O que você procura?" />
</section>

<!-- código omitido -->

Além do input, adicionamos também uma imagem da lupa. Usamos então img e um src (source). O caminho da imagem é assets/imagens/lupa.png. Como essa imagem é apenas decorativa, não adicionamos a propriedade alt, pois, nesses casos, não é obrigatório incluir essa descrição.

<!-- código omitido -->

<section id="busca-container">
<input 
    type="text"
    id="campo-busca"
    placeholder="O que você procura?" />
<img src="assets/imagens/lupa.png" alt="">
</section>

<!-- código omitido -->

Agora, voltamos à aplicação. Observamos que o layout está um pouco desalinhado, com a lupa à direita fora do campo de busca. Para corrigir isso, adicionamos uma classe à imagem da lupa. Definimos a classe como lupa.

<!-- código omitido -->

<section id="busca-container">
<input 
    type="text"
    id="campo-busca"
    placeholder="O que você procura?" />
<img class="lupa" src="assets/imagens/lupa.png" alt="">
</section>

<!-- código omitido -->

Essas classes e IDs já estão sendo referenciados no nosso CSS, em styles.css.

styles.css

# código omitido

#busca-container {
  display: flex;
  align-items: center;
  margin-top: 56px;
  width: 100%; 
  max-width: 600px; 
  margin: 56px auto; 
  padding: 0 16px; 
}

#campo-busca {
  flex: 1; 
  width: 572px;
  height: 48px;
  padding: 8px 16px;
  border-radius: 32px;
  border: 1px solid #144480;
  font-family: "Poppins", sans-serif;
  font-size: 16px;
  font-weight: 400;
  outline: none;
}

.lupa {
  margin-left: -40px;
  width: 24px;
  height: 24px;
}

# código omitido

Estamos estilizando tanto o container quanto o input e a imagem. Assim, ao voltar para a aplicação, observamos o campo de busca com a lupa corretamente posicionada dentro dele.

Conclusão e próximo passo

Completamos o primeiro passo: modificamos a interface para incluir um campo de busca, permitindo que a pessoa digite um termo para realizar a busca. Observamos que não há um botão para buscar, mas isso será abordado em vídeos futuros, onde explicaremos por que apenas o campo de busca é suficiente por enquanto.

No próximo passo, iremos ao arquivo da API, onde já temos algumas requisições configuradas para enviar o termo de busca. A implementação detalhada será discutida no próximo vídeo!

Implementando um campo de busca - Implementando a lógica de busca e filtragem de pensamentos

No vídeo anterior, adicionamos o campo de busca na aplicação. No entanto, ele ainda não está funcionando porque precisamos integrar essa funcionalidade no arquivo api.js, que é responsável por enviar as requisições para a API.

Acessamos o arquivo api.js no VSCode. O que precisamos fazer é adicionar uma nova busca. Já temos algumas buscas implementadas na aplicação, como a função buscarPensamentos(), que retorna todos os pensamentos, e a função buscarPensamentoPorId(id), que retorna um pensamento específico com base no ID.

Criando a função buscarPensamentosPorTermo()

Desta vez, desejamos buscar um conjunto de pensamentos que contenham uma palavra-chave, que a pessoa vai digitar, seja no conteúdo ou na autoria. Para isso, criamos uma nova função. Na linha 58, digitaremos async e chamaremos essa função de buscarPensamentosPorTermo(). Precisamos passar como parâmetro o termo que a pessoa vai digitar.

api.js

// código omitido

async buscarPensamentosPorTermo(termo) {

}

// código omitido

Dentro dessa função, precisamos primeiro obter todos os pensamentos. Após isso, podemos filtrá-los para identificar quais contêm o termo pesquisado. Para obter todos os pensamentos, utilizaremos a função buscarPensamentos(), pois já temos essa funcionalidade implementada e não precisamos fazer uma nova requisição.

Criamos uma constante chamada pensamentos e atribuímos a ela o valor retornado pela função this.buscarPensamentos(), usando await.

// código omitido

async buscarPensamentosPorTermo(termo) {
    const pensamentos = await this.buscarPensamentos()
}

// código omitido

Assim, pensamentos conterá todos os pensamentos, pois a função buscarPensamentos() retorna todos os dados dos pensamentos, conforme definido na linha 4 da função.

Com isso, temos todos os pensamentos armazenados na constante pensamentos.

Criando a constante pensamentosFiltrados

Criaremos uma constante chamada pensamentosFiltrados. Para obter esses pensamentos filtrados, faremos um filtro no array pensamentos. No JavaScript, utilizamos o método filter para essa tarefa, e o nome do método é bem sugestivo para sua função.

Passaremos pensamentos.filter, onde fornecemos um callback que recebe cada pensamento e uma condição. Se o pensamento atender a essa condição, ele será incluído no array pensamentosFiltrados; caso contrário, não será incluído. Inserimos uma arrow function dentro dos parênteses do pensamentos.filter().

// código omitido

async buscarPensamentosPorTermo(termo) {
    const pensamentos = await this.buscarPensamentos()
    
    const pensamentosFiltrados = pensamentos.filter(pensamento => {
    
    })
}

// código omitido

Desejamos que o método filter retorne todos os pensamentos que atendem à condição. Para isso, usaremos return para incluir o pensamento.conteudo. Também aplicaremos o método toLowerCase() a esse conteúdo.

// código omitido

async buscarPensamentosPorTermo(termo) {
    const pensamentos = await this.buscarPensamentos()
    
    const pensamentosFiltrados = pensamentos.filter(pensamento => {
    return pensamento.conteudo.toLowerCase()
    })
}

// código omitido

Esse método converte o texto para letras minúsculas para garantir que a busca não seja case-sensitive (sensível a maiúsculas e minúsculas). Assim, asseguramos que o termo de busca seja encontrado independentemente de como o texto está formatado.

Desejamos que, se a pessoa digitar "chaves" em minúsculas, maiúsculas ou caixa alta, isso não influencie na busca. Para garantir isso, converteremos o conteúdo digitado e o conteúdo dos pensamentos para letras minúsculas. Assim, a busca será realizada de forma uniforme, independentemente do formato das letras.

Para converter o termo digitado para letras minúsculas, criaremos uma constante chamada termoEmMinusculas após pensamentos, que receberá o valor de termo.toLowerCase().

// código omitido

async buscarPensamentosPorTermo(termo) {
    const pensamentos = await this.buscarPensamentos()
    const termoEmMinusculas = termo.toLowerCase()
    
    const pensamentosFiltrados = pensamentos.filter(pensamento => {
    return pensamento.conteudo.toLowerCase()
    })
}

// código omitido

Assim, independentemente de como a pessoa digitar o termo, ele será convertido para minúsculas. Compararemos esse termo convertido com o conteúdo do pensamento, que também será convertido para minúsculas, garantindo uma busca consistente.

Verificando o termo no conteúdo

Após a comparação, desejamos verificar se o conteúdo do pensamento inclui o termo pesquisado. Para isso, usaremos o método includes() do JavaScript após o toLowerCase(). Esse método verifica se o termo está presente no conteúdo, retornando true se estiver e false se não estiver. Passaremos o termoEmMinusculas para essa verificação.

// código omitido

async buscarPensamentosPorTermo(termo) {
    const pensamentos = await this.buscarPensamentos()
    const termoEmMinusculas = termo.toLowerCase()
    
    const pensamentosFiltrados = pensamentos.filter(pensamento => {
    return pensamento.conteudo.toLowerCase().includes(termoEmMinusculas)
    })
}

// código omitido

Vamos criar um filtro que permita pesquisar tanto pelo conteúdo quanto pela autoria. Digitamos duas barras verticais (||), que representam o operador "ou". Abaixo, inserimos o código: pensamento.autoria.toLowerCase().includes(termoEmMinusculas).

Desejamos que sejam retornados os pensamentosFiltrados.

// código omitido

async buscarPensamentosPorTermo(termo) {
    const pensamentos = await this.buscarPensamentos()
    const termoEmMinusculas = termo.toLowerCase()
    
    const pensamentosFiltrados = pensamentos.filter(pensamento => {
    return pensamento.conteudo.toLowerCase().includes(termoEmMinusculas) || pensamento.autoria.toLowerCase().includes(termoEmMinusculas)
    })
    return pensamentosFiltrados
}

// código omitido

Após obter esses resultados, adicionaremos o tratamento de erros.

Adicionando o tratamento de erros

Para manter o padrão utilizado em outras requisições, usaremos o bloco try-catch. Na linha 59, digitamos try e abrimos os colchetes. Movemos o conteúdo da linha 62 até a linha 69 para dentro do bloco try (todo o código que montamos).

Na linha 68, após o primeiro fechamento de chaves depois do return, adicionamos um catch(error). Se ocorrer um erro, usamos alert() para informar a pessoa usuária com a mensagem "Erro ao filtrar pensamentos". Em seguida, lançamos o erro com throw error.

// código omitido

async buscarPensamentosPorTermo(termo) {
    try {
        const pensamentos = await this.buscarPensamentos()
        const termoEmMinusculas = termo.toLowerCase()

        const pensamentosFiltrados = pensamentos.filter(pensamento => {
            return pensamento.conteudo.toLowerCase().includes(termoEmMinusculas) ||
            pensamento.autoria.toLowerCase().includes(termoEmMinusculas)
        })
        return pensamentosFiltrados
    } catch (error) {
        alert("Erro ao filtrar pensamentos")
        throw error
    }
} 

// código omitido

Conclusão e próximo passo

Com isso, temos uma nova requisição na API responsável pela lógica de filtragem e pela verificação de se os pensamentos contêm o termo digitado pela pessoa.

No entanto, ao voltar à aplicação e digitar qualquer coisa, não veremos a atualização da lista ainda, pois precisamos fazer algumas modificações na interface. Abordaremos essas alterações nos próximos vídeos!

Sobre o curso JavaScript: evoluindo a sua aplicação com ES6+

O curso JavaScript: evoluindo a sua aplicação com ES6+ possui 133 minutos de vídeos, em um total de 61 atividades. Gostou? Conheça nossos outros cursos de JavaScript em Front-end, ou leia nossos artigos de Front-end.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda JavaScript acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas