DEV Community

loading...
Cover image for QuemMeVueMentiu 01 - Criando uma Calculadora - Projeto em Vue.js

QuemMeVueMentiu 01 - Criando uma Calculadora - Projeto em Vue.js

William Gonçalves
Desenvolvimento Web e afins | JavaScript, CSS, React e Vue.js | Postador de Meme e aspirante a Youtuber.
・10 min read

Primeira contribuição da série QuemMeVueMentiu que trará artigos e vídeos criando projetos em Vue.js, mostrando algumas características da ferramenta.


E o que é o Vue.js?

"Vue (pronuncia-se /vjuː/, como view, em inglês) é um framework progressivo para a construção de interfaces de usuário. Ao contrário de outros frameworks monolíticos, Vue foi projetado desde sua concepção para ser adotável incrementalmente. A biblioteca principal é focada exclusivamente na camada visual (view layer), sendo fácil adotar e integrar com outras bibliotecas ou projetos existentes. Por outro lado, Vue também é perfeitamente capaz de dar poder a sofisticadas Single-Page Applications quando usado em conjunto com ferramentas modernas e bibliotecas de apoio." (Site Oficial)

"Foi criado pelo desenvolvedor independente chinês Evan You, após trabalhar para o Google usando o AngularJS em vários projetos. Posteriormente, ele resumiu o pensamento por trás do processo de criação do Vue: "Pensei, e se eu pudesse retirar somente a parte que realmente gostava do Angular e criar algo muito leve?". [9] O primeiro commit ao código-fonte do projeto data de julho de 2013, e o framework foi lançado oficialmente em fevereiro do ano seguinte, em 2014." (Wikipédia)

Você pode saber mais no guia oficial


E por que vamos usar o Vue.js nesse projeto?

Por conta do template com vários botões iguais, vamos usar uma diretiva nativa do Vue.js para iterar uma lista de dados que retornará os botões diretamente no template.

Além disso, para minimizar o código e a manipulação direta dos elementos do DOM, vamos trabalhar as operações dentro dos scripts, exibindo as informações de forma reativa na tela da calculadora.


Arquivos necessários

Vamos criar três arquivos, na mesma pasta, para nosso projeto:

  • index.html
  • style.css
  • script.js

index.html

Vamos começar com a estrutura básica da página:

<!DOCTYPE html>
<html lang="pt-br">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>QuemMeVueMentiu 01 - Criando uma Calculadora - Projeto em Vue.js</title>
</head>
<body>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Importando o Vue.js para o projeto

Uma das grandes vantagens do Vue.js é a facilidade de integração em projetos menores e até em projetos existentes.

Em casos em que vamos utilizar suas funcionalidades em um escopo controlado, sem necessidade de rotas, etc, basta importar o framework, através de uma tag script diretamente em nosso HTML, acima da tag de fechamento </body>.

Além dela, já importamos nosso arquivo script.js.

  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="script.js"></script>
</body>
Enter fullscreen mode Exit fullscreen mode

Testando o Vue.js

Para testar o funcionamento do Vue.js, vamos incluir dois elementos temporários, dentro de um container (#app), logo abaixo da tag <body>.

No <h1>, o conteúdo será exibido através da variável message, usando as duplas chaves ({{ }}), que será atribuída a diretiva v-model do input. Essa diretiva serve para manter os dados sincronizados em mão dupla: o input atualiza o dado, assim como o dado, sendo alterado, atualiza o input. Como o nome sugere, o input será um modelo do dado declarado na diretiva.

<body>
  <div id="app">
    <h1>{{ message }}</h1>
    <input type="text" v-model="message">
  </div>
Enter fullscreen mode Exit fullscreen mode

No arquivo script.js iniciaremos nossa instância Vue, relacionando-a a div com id="app", através da propriedade el, além de incluir a variável message no objeto data, onde devemos declarar nossas variáveis que serão utilizadas na aplicação. Começaremos com uma mensagem padrão.

var app = new Vue({
  el: "#app",
  data: {
    message: "Mas já está reativo? :O",
  },
});
Enter fullscreen mode Exit fullscreen mode

Salvando as alterações, podemos abrir o index.html no navegador e ver o Vue.js funcionando na prática:

Reatividade em Funcionamento


Vamos para o projeto?

index.html

No index.html, começaremos linkando nosso style.css, assim como a fonte que usaremos no nosso app, a ZCOOL QingKe HuangYou, acima da tag de fechamento </head>.

  <link rel="stylesheet" href="style.css">
  <link href="https://fonts.googleapis.com/css2?family=ZCOOL+QingKe+HuangYou&display=swap" rel="stylesheet">
</head>
Enter fullscreen mode Exit fullscreen mode

Mantendo nossa <div id="app">, removemos os elementos de teste e adicionamos a div 'calculator', com uma <textarea> que servirá como tela, além da div para incluirmos nosso logo e de um span, que servirá como base para os botões.

Na <textarea>, incluímos a propriedade readonly, para impedir digitação de valores, além da diretiva v-model relacionada a variável screenValue, que será criada nos scripts.

<div id="app">
    <div class="calculator">
      <textarea type="text " class="screen" v-model="screenValue" name="txt" readonly></textarea>
      <div class="logo"></div>
      <span class="key"></span>
    </div>
  </div>
Enter fullscreen mode Exit fullscreen mode

Em seguida, ainda no HTML, importamos a biblioteca Math.js, de onde usaremos um método para calcular a expressão da tela.

Como a ideia do projeto é explorar algumas funcionalidades do Vue.js, aprofundar nessa funcionalidade tomaria tempo e nos desviaria do foco.

Por isso, vamos aproveitar o que a comunidade nos oferece! :D

Importe a Math.js, acima da importação do Vue.js. Deve ficar assim:

  <script src="https://unpkg.com/mathjs@7.5.1/dist/math.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="script.js"></script>
</body>
Enter fullscreen mode Exit fullscreen mode

script.js - variáveis

No script.js, removeremos a mensagem de teste do objeto data, incluindo as variáveis que serão usadas no nosso app:

  • isLastInputANumber: booleano que mudará sua condição em função das entradas na tela. Serve para controlar a entrada de operadores nas expressões: se o último input for um número, você pode adicionar um operador. Adicionando um operador, a variável mudará seu estado para falso. Isso impedirá que dois operadores sejam adicionados em sequência, o que quebraria a expressão.

  • isResultDisplayed: booleano que mudará sua condição quando o usuário pressionar o botão "=", para exibir o resultado na tela. Ela é usada como controle para a entrada de número: se o resultado estiver na tela e um número for adicionado, o app iniciará uma nova expressão. Caso contrário, o número será adicionado a expressão em andamento.

  • inputValues: um Array com os valores que serão exibidos nos botões. Lembra que criamos um span para o botão? Usaremos um recurso do Vue.js para recriar uma cópia desse span, para cada valor nesse Array.

  • screenValue: o valor exibido na tela. Será onde os dados serão adicionados e a expressão será calculada.

  data: {
    isLastInputANumber: false,
    isResultDisplayed: false,
    inputValues: [
      "C",
      "/",
      "7",
      "8",
      "9",
      "*",
      "4",
      "5",
      "6",
      "-",
      "1",
      "2",
      "3",
      "+",
      "0",
      "00",
      ".",
      "=",
    ],
    screenValue: "",
  },
Enter fullscreen mode Exit fullscreen mode

script.js - métodos

Vamos declarar as funções utilizados na nossa aplicação. Elas também são inseridos em um objeto: o methods. Esse objeto deve vir declarado abaixo do objeto data, como no exemplo abaixo:

var app = new Vue({
  el: "#app",
  data: {
    // variáveis
  },
  methods: {
    // métodos
  },
});
Enter fullscreen mode Exit fullscreen mode

A primeira função será a clearScreen(), que limpará a tela e retornará a aplicação a seu estado inicial. Note que, por estarmos dentro de um escopo controlado, uma instância do Vue.js, devemos referenciar a localização das variáveis, através do prefixo this..

A segunda função será a inputOperator() que terá inputValue, o operador digitado, como parâmetro. Ela verificará se a última entrada foi um número e, caso positivo, adicionará o operador.

A terceira função será a inputNumber() que terá inputValue, o número digitado, como parâmetro. Ela verificará se o valor na tela é o resultado de uma expressão. Caso verdadeiro, iniciará uma nova expressão com o número digitado. Caso contrário, adicionará o número a expressão em andamento.

E a quarta e última função será a displayResult(), que verificará se há algum valor na tela e, caso positivo, substituirá a expressão exibida pelo seu resultado, através do método evaluate() da biblioteca Math.js.

O objeto methods ficará assim:

  methods: {
    clearScreen() {
      this.isLastInputANumber = false;
      this.isResultDisplayed = false;
      this.screenValue = "";
    },
    inputOperator(inputValue) {
      if (this.isLastInputANumber) {
        this.screenValue += inputValue;
        this.isLastInputANumber = false;
        this.isResultDisplayed = false;
      }
    },
    inputNumber(inputValue) {
      if (this.isResultDisplayed) {
        this.screenValue = inputValue;
        this.isResultDisplayed = false;
        this.isLastInputANumber = true;
      } else {
        this.screenValue += inputValue;
        this.isLastInputANumber = true;
      }
    },
    displayResult() {
      if (this.screenValue) {
        this.screenValue = math.evaluate(this.screenValue);
        this.isResultDisplayed = true;
      }
    },
  },
Enter fullscreen mode Exit fullscreen mode

index.html - preenchendo o template

Lembra do nosso <span class="key"></span>? Vamos iterar o array inputValues com a diretiva v-for, para criar os botões, além de usar as diretivas v-bind (sobre a classe) e v-on (sobre o evento de click), para preencher dinamicamente as classes e os métodos atribuídos a cada botão.

Como nosso primeiro botão (index = 0 no array) é o 'C', que apaga a tela, ele será atribuído a função clearScreen() e terá duas classes adicionadas: wide, que fará com que ele tenha o dobro do tamanho dos outros botões, e operator, que o deixará com uma cor diferente dos botões padrão.

Nosso último botão (index = 17 no array) é o '=', que exibe o resultado da expressão na tela. A ele, será atribuída a função displayResult(). A classe operator também deverá ser adicionada a ele, mudando sua cor.

A cada quatro posições, a partir do index = 1, temos nossos operadores. Para encontrá-los no array, somaremos '3' ao valor do index, para que possamos usar o operador '%', verificando se a expressão index + 3 % 4 retorna zero como resto de uma divisão. Caso positivo, a posição representa um operador da nossa calculadora. Atribuiremos a função inputOperator(input), onde input é o valor do botão pressionado, além de incluir a classe operator, que mudará a cor do botão.

Caso nenhuma das condições acima sejam verdadeiras, para a posição lida no array inputValues, significa que ela é um número. Com isso, poderemos atribuir a ela a função inputNumber(input), onde input é o valor do botão pressionado. Ela permanecerá com a classe padrão key.

Como dito acima, vamos iterar o array inputValues com a diretiva v-for. Ela retornará cada posição como input, além do seu index, que nos ajudará a verificar as condições descritas acima. Para cada posição, um novo <span> será renderizado com valores, classes e métodos dinâmicos:

<span 
  class="key" 
  v-for="(input, index) of inputValues" 
  v-bind:class="index == 0 || index == 17 || (index + 3) % 4 == 0 
                ? index == 0
                  ? 'operator wide'
                  : 'operator'
                : ''"
  v-on:click="index == 0 ? clearScreen()
              : index == 17 ? displayResult()
              : (index + 3) % 4 == 0 && index != 17 ? inputOperator(input)
              : inputNumber(input)"
>
  {{ input }}
</span>
Enter fullscreen mode Exit fullscreen mode

O funcionamento já poderá ser visto, salvando as alterações e abrindo o index.html no navegador:

Calculadora em Funcionamento


Usando o Dev Tools, é possível ver que os botões foram renderizados dinamicamente:

Árvore DOM - Botões Preenchidos com v-for


style.css - dando um rosto a nossa calculadora

Incluirei as estilizações, passo a passo.

Recomendo que, a cada uma delas, você salve o resultado e veja no navegador, acompanhando a evolução do design.

No nosso arquivo style.css, começamos com os resets de espaçamento, definimos a tela com 100% de altura e definimos a fonte que importamos anteriormente:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  font-family: "ZCOOL QingKe HuangYou", sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

Em seguida, fazemos nossa div #app ocupar toda a tela, definimos sua cor de fundo e declaramos suas propriedades como flex-container, centralizando a calculadora no centro do app.

#app {
  width: 100%;
  height: 100%;
  padding: 20px 0;
  background: #121212;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
Enter fullscreen mode Exit fullscreen mode

Definimos as medidas da calculadora. Usaremos CSS Grid para distribuir os botões. O template do grid será de quatro colunas. Usaremos a função repeat para replicar o valor 1fr quatro vezes.

.calculator {
  width: 320px;
  height: 560px;

  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
Enter fullscreen mode Exit fullscreen mode

Definimos a tela ocupando o espaço de quatro colunas do grid, adicionamos um espaçamento interno, estilizações de borda, cores e o tamanho da fonte, além de impedirmos que ela seja redimensionada, com a propriedade resize:

.calculator .screen {
  grid-column: span 4;
  width: 100%;
  border: 0;
  padding: 10px;

  border-radius: 10px;
  background: #35495e;
  color: #fff;

  font-size: 3em;
  text-align: right;
  resize: none;
  font-family: "ZCOOL QingKe HuangYou", sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

Definimos a área do logo com o ícone do Vue.js. A url utilizada no background foi retirada do site Devicon:

.calculator .logo {
  margin: 4px;

  background: url("https://raw.githubusercontent.com/devicons/devicon/master/icons/vuejs/vuejs-original.svg")
    no-repeat center;
  background-size: 70%;
}
Enter fullscreen mode Exit fullscreen mode

Definimos o padrão dos botões, desde bordas, cores, sombras, até o display flex, alinhando os valores ao centro. Duas transições são inclusas, para os efeitos que virão a seguir.

.calculator .key {
  border-radius: 10px;
  margin: 4px;

  cursor: pointer;
  background: #42b883;
  color: #fff;
  box-shadow: 2px 2px 4px #215c41;

  font-size: 1.5rem;

  display: flex;
  justify-content: center;
  align-items: center;

  transition: transform 0.1s, background-color 0.1s;
}
Enter fullscreen mode Exit fullscreen mode

Adicionamos um efeito para quando o mouse estiver sobre o botão, mudando sua cor...

.calculator .key:hover {
  background: #184430;
}
Enter fullscreen mode Exit fullscreen mode

...além de um efeito para quando o botão for clicado, que fará ele se deslocar em direção a sombra, dando a impressão de que está afundando, quando pressionado.

.calculator .key:active {
  transform: translate(2px, 2px);
}
Enter fullscreen mode Exit fullscreen mode

Por último, incluímos as duas classes adicionais para mudar as cores dos operadores e para aumentar o botão 'C'. Os seletores são declarados da mesma forma que os da classe .key, para que as declarações acima, com as pseudo-classes :hover e :active tenham maior especificidade e sobrescrevam, também, os estilos desses botões com cor padrão diferente.

.calculator .operator {
  background: #276b4c;
}

.calculator .wide {
  grid-column: span 2;
}
Enter fullscreen mode Exit fullscreen mode

E olha que lindinha, nossa calculadora:

Projeto da Calculadora em Vue.js Finalizado


Você pode usar o app através deste link


E aí? Já conhecia o Vue.js? Curtiu criar esse projeto do zero?

Deixe seu comentário ou sua sugestão.

E se você curtiu esse conteúdo, compartilhe com outras pessoas e ajude a espalhar a palavra do Mago!


Você pode me acompanhar nas minhas redes sociais:

Instagram
GitHub
LinkedIn
Rocketseat
YouTube

E conhecer mais sobre mim e meu trabalho no meu site:

mago.link


Nos vemos na próxima mágica! 🧙‍♂️

Discussion (0)