roger-melo-treinamentos / curso-de-js-roger-melo

Repositório de informações do CJRM
494 stars 172 forks source link

Aplicação: Quiz Interativo - Exercício Aula 1 Etapa 7 #1959

Closed ElaineDelgado closed 2 years ago

ElaineDelgado commented 2 years ago

As cores da sua versão da aplicação são diferentes das cores da aplicação mostrada na aula?

Sim

A aplicação contém funcionalidades que não foram mostradas nas aulas?

Não

A aplicação contém features da linguagem que não foram mostradas nas aulas?

Não

Link do repositório da aplicação

https://github.com/ElaineDelgado/quizCJRM

Maiores dificuldades durante a implementação

Saber onde declarar const, se dentro ou fora da função; Refatorar o código, sempre acho que a legibilidade está boa.

Menores dificuldades durante a implementação

Lógica do app, Implementação do código funcional; HTML e CSS, Bootstrap.

Roger-Melo commented 2 years ago

Olá @ElaineDelgado.

Passando para dizer que visualizei a issue e se estiver tudo ok com a aplicação, em breve a análise será postada aqui =)

Roger-Melo commented 2 years ago

Olá @ElaineDelgado!

Parabéns pelo esforço em construir a primeira aplicação do treinamento =)

Saber onde declarar const, se dentro ou fora da função;

Um princípio que gosto de seguir é: deixe as declarações das suas variáveis perto de onde elas são usadas.

Porém, nem sempre esse princípio deve ser aplicado. Por questões de escopo.

Um exemplo clássico é quando vc tem duas funções que precisam acessar a mesma variável.

No código abaixo, func1 e func2 acessam myConst normalmente:

const myConst = 'my value'

const func1 = () => {
  console.log(myConst)
}

const func2 = () => {
  console.log(myConst)
}

func1() // 'my value'
func2() // 'my value'

Porém, se myConst é declarada dentro de func1, func2 não consegue acessar myConst por que myConst agora é acessível apenas dentro do escopo de func1:

const func1 = () => {
  const myConst = 'my value'
  console.log(myConst)
}

const func2 = () => {
  console.log(myConst)
}

func1() // 'my value'
func2() // app.js:7 Uncaught ReferenceError: myConst is not defined

Esse é um caso onde myConst precisa estar em escopo global, para que ela seja acessada dentro das duas funções.

Mais à frente, vc começará a ver por que variáveis globais não são uma boa ideia. Mas por enquanto, isso não é importante.

Um outro caso que vc precisa considerar é o escopo de bloco...

Lembra que não há problema em duas consts de mesmo nome serem criadas em escopos diferentes?

No código abaixo,

const myFunc = () => {
  const myConst = 'my value'
  console.log(myConst)
}

myFunc()
myFunc()

a cada vez que myFunc é invocada, o bloco que contém a declaração da const e o console.log é executado, certo?

Blocos criam escopos.

Isso significa que na primeira vez que myFunc foi invocada, um escopo com a const myConst foi criado. E na segunda vez em que ela foi invocada, um outro escopo com uma outra const myConst foi criado.

Ou seja, se myFunc é invocada 2x, duas consts myConst foram criadas em memória.

E isso é importante pelo seguinte...

Copie o código abaixo e execute ele:

<div></div>
<button>Adicionar parágrafo</button>
const div = document.querySelector('div')
const button = document.querySelector('button')

const paragraph = document.createElement('p')

button.addEventListener('click', () => {
  div.appendChild(paragraph)
})

Ao executar esse código e clicar no botão pela primeira vez, vc verá que um <p> fio adicionado dentro da <div>.

Porém, do segundo clique em diante, não são adicionados novos <p>.

Por que isso acontece?

Isso acontece por que a cada execução do callback de clique, o paragraph ao invés de ser criado novamente está apenas sendo reaproveitado.

Ou seja, como paragraph foi declarado fora do escopo do callback, a cada execução do bloco do callback, o mesmo parágrafo está sendo inserido dentro da div.

Lembre-se: quando um callback de listener de evento é executado, ele não faz o código que está fora dele ser declarado novamente. Apenas o bloco do callback é executado novamente. Isso significa que o mesmo paragraph está sendo usado dentro do callback.

Agora, se paragraph for declarado dentro do callback,

button.addEventListener('click', () => {
  const paragraph = document.createElement('p')
  div.appendChild(paragraph)
})

toda vez que esse bloco do callback for executado, um novo paragraph será criado em memória e inserido como filho da div. Ou seja, o segundo clique no botão irá inserir um segundo <p> como filho da div, um terceiro clique irá inserir um terceiro <p> e assim por diante. Graças ao escopo de bloco =)

Ficou mais claro?


Refatorar o código, sempre acho que a legibilidade está boa.

De fato, o ideal é não refatorar apenas por refatorar.

Porém, legibilidade e dinamicidade podem andar juntas.

O código abaixo é legível, porém, repetitivo:

  const userAnswers = [
    form.anime1.value,
    form.anime2.value,
    form.anime3.value,
    form.anime4.value,
    form.anime5.value,
    form.anime6.value,
    form.anime7.value,
    form.anime8.value,
    form.anime9.value,
    form.anime10.value,
  ]

Nas aulas seguintes, vc verá como eliminar repetições como essa =)

Vou deixar abaixo alguns pontos, segundo os critérios que usei para analisar.


Boas decisões

Você entendeu que não era necessário manipular DOM dentro do forEach (um erro comum que algumas pessoas cometem). A ideia do forEach ali naquele momento era apenas calcular os pontos, exatamente como vc fez =)

O parâmetro do callback do forEach foi nomeado de forma coerente, como singular de itens do array no qual o forEach foi encadeado.

Você deixou claro o total de pontos que o quiz tem na mensagem "Você fez ${totalScore} pontos de 100!". Isso é bom para a usabilidade, pq traz clareza do progresso do usuário no quiz.

No geral, você escolheu nomes descritivos para suas variáveis e funções.


Lógica

Algumas alternativas estão com o valor do atributo value trocado. Por exemplo, na 1ª pergunta, a segunda alternativa tem o value recebendo 3 e a terceira tem o value recebendo 2. Isso pode deixar a aplicação imprevisível devido à ser presumido naturalmente que o value de cada alternativa corresponde à posição dela.

Observe que se vc descer até a última pergunta, verá que a primeira alternativa está marcada por padrão. Porém, se vc marcar a alternativa 1 da antepenúltima pergunta, verá que agora a última pergunta não tem mais a primeira alternativa marcada. Isso aconteceu devido a conflitos de marcação HTML, onde o valor de id ou name não correspondem ao for correto da label em questão.

Usabilidade

Local da exibição dos pontos

Talvez, depois de você hospedar o quiz no Netlify (como orientado na bateria de exercícios 20), alguém execute a aplicação no celular. Nesse caso, é preciso ficar atenta ao o local onde a pontuação é exibida na tela.

Imagina que, por algum motivo, o usuário não visualizou a parte inferior ao botão de envio. No print abaixo, após enviar o form, a pontuação é exibida na parte inferior (não-visível) da tela:

image

Uma possível solução seria exibir os pontos em um local em que o usuário está olhando no momento do envio. Poderia ser, por exemplo, logo acima do botão "enviar" =)

E claro, nas aulas seguintes você verá uma forma de implementar os pontos.

Organização de código

Idioma das variáveis

A const btnVoltarInicio está em pt-BR enquanto o restante das variáveis está em inglês. O ideal é que apenas um idioma seja usado =)

Nomes de variáveis e funções

Um princípio que gosto de seguir ao nomear é:

Considerando isso...

totalScorePoints é uma função que foi nomeada como substantivo. showScore é um verbo que pode descrever com mais clareza o que essa função faz.


Nas aulas seguintes, você verá formas de refatorar o código dessa aplicação.

Mais uma vez, parabéns pelo esforço =)

As observações fizeram sentido?

ElaineDelgado commented 2 years ago

Bom dia, professor! Sim, a explicação fez bastante sentido. Em relação à lógica onde o senhor diz: ". Porém, se vc marcar a alternativa 1 da antepenúltima pergunta, verá que agora a última pergunta não tem mais a primeira alternativa marcada. " Isso foi um erro meu no html, realmente, o "id" e o "name" estão com valor"anime8" em vez de "anime10". Falta de atenção minha. E em relação ao "Algumas alternativas estão com o valor do atributo value trocado. " , foi intencional pois se eu colocasse na ordem, todas as perguntas teriam a teriam a terceira opção como correta, não consegui pensar em uma forma de deixar os "value" em ordem mas na aplicação trocar a ordem das alternativas corretas.

Muito obrigada pelas explicações!!

Roger-Melo commented 2 years ago

não consegui pensar em uma forma de deixar os "value" em ordem mas na aplicação trocar a ordem das alternativas corretas

O ponto principal está no array correctAnswers.

Mas antes de chegar nele, preciso que vc execute o passo passo abaixo comigo, ok?

Copie e execute o código abaixo:

  <form>
      <h2>Pergunta</h2>

      <div>
        <label>
          <input type="radio" name="inputQuestion1" value="1">
          Alternativa 1
        </label>
      </div>
      <div>
        <label>
          <input type="radio" name="inputQuestion1" value="2" checked>
          Alternativa 2
        </label>
      </div>
      <div>
        <label>
          <input type="radio" name="inputQuestion1" value="3">
          Alternativa 3
        </label>
      </div>
    </div>
  </form>

Vc verá que a alternativa 2 da pergunta está marcada. E ela está marcada por que o atributo checked foi inserido nela. Se o clique acontecer em outra alternativa, o input correspondente recebe o atributo checked, correto?

Imagine que no momento em que o form foi enviado, a alternativa 2 foi marcada como a alternativa correta.

Então de alguma forma, vc precisa, no seu código js, obter qual das 3 alternativas foi marcada como a correta, certo?

Observe que os 3 inputs da alternativa contém name="inputQuestion1". Eles tem esse atributo pq os 3 estão relacionados à pergunta 1. Se houvesse uma pergunta 2, os inputs dessa pergunta teriam name="inputQuestion2".

Para saber qual alternativa da pergunta está marcada no momento do envio, basta especificar form.inputQuestion1.value:

const form = document.querySelector('form')
console.log(form.inputQuestion1.value) // 2

Ou seja, a expressão form.inputQuestion1.value resulta no valor do atributo value do input que contém o atributo checked.

Como vc sabe que a alternativa correta é a que está marcada (2), é fácil fazer uma comparação que ative a soma de pontos ao usuário:

const form = document.querySelector('form')
let score = 0

if (form.inputQuestion1.value === '2') {
  score = 10
}

console.log(score) // 10

Só que imagina que ao invés de uma pergunta, houvessem 10.

Como vc vai dizer, no seu código js, qual é a alternativa correta de cada pergunta?

É aqui que entra o array correctAnswers.

Se a alternativa correta da pergunta 1 é a que contém o value do input armazenando 2, o primeiro item do array será 2:

const form = document.querySelector('form')

const correctAnswers = ['2']
let score = 0

if (form.inputQuestion1.value === correctAnswers[0]) {
  score = 10
}

console.log(score) // 10

Se a alternativa correta da pergunta 2 é a que contém o value do input armazenando 3, o segundo item do array será 3. Então ficaria const correctAnswers = ['2', '3'].

Ficou mais claro?

@ElaineDelgado

ElaineDelgado commented 2 years ago

Ficou bem claro, sim. Não tinha pensado em colocar elementos diferentes na array correctAnswers, faz mais sentido que ordenar aleatoriamente no HTML. Muito obrigada, professor!

Roger-Melo commented 2 years ago

Show! No que precisar, é só abrir uma nova issue. Rumo à fluência! 🔥