Desenvolvimento para dispositivos móveis

Notas de aula do prof. Eduardo da disciplina de Desenvolvimento para Dispositivos Móveis (curso técnico)

View on GitHub

Chamada da API

Inicialmente, vamos criar um arquivo .env na raiz do projeto com a seguinte configuração:

VITE_BACKEND_BASE_URL=http://localhost:8000

Quanto usamos o Vite para gerenciar nossos projetos, podemos criar variáveis de ambiente prefixadas com VITE_ e acessá-las no código usando import.meta.env.VITE_NOME_DA_VARIAVEL. Neste caso, estamos criando uma variável chamada VITE_BACKEND_BASE_URL que armazena a URL base do nosso backend.

Feito isso, vamos realizar com a configuração global do pacote axios. Para isso, vamos criar o arquivo src/plugins/axios.js com o seguinte conteúdo:

import axios from 'axios';
const BASE_URL = import.meta.env.VITE_BACKEND_BASE_URL;

axios.defaults.baseURL = `${BASE_URL}/api/`;

Note que estamos configurando a URL base da API FakeStore desenvolvida em Django, como informando no arquivo .env. Você pode querer usar outro servidor ou baixar o projeto e rodar localmente. Para isso, você precisará alterar apenas a URL base.

Em seguida, vamos criar uma classe para consultar a API. Para isso, vamos criar o arquivo src/services/product.js com o seguinte conteúdo:

import axios from 'axios';

export default class ProductService {
  async getProducts() {
    const response = await axios.get('/products/');
    return response.data.results;
  }

  async getProductByCategory(category_id) {
    const response = await axios.get(`/products/?category__id=${category_id}`);
    return response.data.results;
  }
}

Neste caso, estamos criando um serviço para consultar a API FakeStore. Esse serviço possui dois métodos: getProducts que retorna todos os produtos e getProductByCategory que retorna os produtos de uma categoria específica.

Configurando o serviço de gerenciamento de estados

Nos nossos exemplos, usaremos o gerenciamento de estados, com o Pinia, para fazer a interface entre a API e os componentes. Para isso, vamos criar o arquivo src/stores/product.js com o seguinte conteúdo:

import { ref } from 'vue';
import { defineStore } from 'pinia';

import ProductService from '@/services/product';
const productService = new ProductService();

export const useProductStore = defineStore('product', () => {
  const products = ref([]);

  async function getProducts() {
    products.value = await productService.getProducts();
  }

  async function getProductsByCategory(category_id) {
    products.value = await productService.getProductByCategory(category_id);
  }

  return { products, getProducts, getProductsByCategory };
});

Neste arquivo, estamos criando um store para gerenciar os produtos. Esse store possui um objeto reativo products para armazenar os produtos. Além disso, possui dois métodos (ou actions): getProducts que chama o método getProducts do serviço e atualiza a lista de produtos e getProductsByCategory que chama o método getProductByCategory do serviço e atualiza a lista de produtos, nesse caso filtrando por categoria.

Criando funções para formatação dos resultados

Se mandarmos listar os produtos diretamente na tela, veremos que a descrição é muito grande e o preço não está formatado. Para resolver isso, vamos criar funções de formatação. Para isso, vamos criar o arquivo src/helpers/format.js com o seguinte conteúdo:

export const formatPrice = (price) =>
  `R$ ${price.toFixed(2).replace('.', ',')}`;
export const formatTitle = (title) => title.slice(0, 15) + '...';
export const formatDescription = (description) =>
  description.slice(0, 40) + '...';

Nesse caso estamos criando três funções de formatação: formatPrice que formata o preço do produto, formatTitle que limita o título do produto a 15 caracteres e formatDescription que limita a descrição do produto a 40 caracteres.

Configurando o componente de listagem de produtos

Vamos criar o componente de listagem de produtos. Para isso, vamos criar o arquivo src/components/ProductList.vue com o seguinte conteúdo:

<script setup>
import { onMounted } from 'vue';
import { useProductStore } from '@/stores/product';

import { formatDescription, formatPrice, formatTitle } from '@/helpers/format';

const productStore = useProductStore();

async function getProducts() {
  await productStore.getProducts();
}

onMounted(async () => {
  await getProducts();
});
</script>

<template>
  <div class="product-list">
    <div v-if="productStore.products.length === 0">
      <p>Produtos não encontrados!!!</p>
    </div>
    <div
      v-for="product in productStore.products"
      :key="product.id"
      class="product-card"
    >
      <div class="product-img-wrapper">
        <img :src="product.image?.url" alt="product.name" />
        <i class="mdi mdi-heart-outline" />
      </div>
      <div class="product-title-price">
        <p></p>
        <p></p>
      </div>
      <div class="product-description-stars">
        <p></p>
        <div class="stars">
          <i class="mdi mdi-star" size="20" />
          <i class="mdi mdi-star" size="20" />
          <i class="mdi mdi-star" size="20" />
          <i class="mdi mdi-star" size="20" />
          <i class="mdi mdi-star" size="20" />
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.product-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.5rem;
  padding: 1rem;
}

.product-card {
  width: 225px;
  font-family: 'Belleza', sans-serif;
}

.product-img-wrapper {
  display: flex;
  justify-content: center;
  align-items: top;
  gap: 0.5rem;
  padding-top: 20px;
  margin-bottom: 1rem;
  background-color: #eeeeee;
  height: 201px;
}

.product-img-wrapper img {
  width: 153px;
  height: 170px;
  object-fit: cover;
}

.product-title-price {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
}

.product-title-price p {
  font-weight: bold;
  font-size: 16px;
  color: #010101;
}

.product-description-stars {
  display: flex;
  flex-direction: column;
  margin-bottom: 1rem;
}

.product-description-stars p {
  font-size: 12px;
  color: #535050;
}
</style>

Configurando o componente de HomeView

Para finalizar, vamos configurar o componente HomeView.vue para exibir a listagem de produtos. Para isso, vamos editar o arquivo src/views/HomeView.vue com o seguinte conteúdo:

<script setup>
import ProductList from '@/components/ProductList.vue';
</script>

<template>
  <product-list />
</template>

Neste arquivo, estamos importando o componente ProductList e exibindo-o na tela.

< A loja virtualAjustes no cabeçalho e rodapé >