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

Repositório de informações do CJRM
491 stars 170 forks source link

Aplicação: APPWeather #6714

Closed EneasFeynlinus closed 1 year ago

EneasFeynlinus commented 1 year ago

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

Sim

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

Não

Sua aplicação contém funcionalidades da linguagem que não foram mostradas no CJRM até aqui?

Não

Link do repositório da aplicação (ou pasta pública no Google Drive)

https://github.com/EneasFeynlinus/APPWeather

Maiores dificuldades durante a implementação

Fatoração das funções fazendo o request da cityData e entregando os valores da propriedade Key pra city Weather, assim como as futuras transformações ocorridas na aplicação durante a refatoração. Tambem o uso da data-js para referenciar os elementos e adicionar as informações e as imagens. E a uma outra duvida, ao finalizar a aplicação, consigo fazer o request quando executo no meu vsCode. No entanto quando faço o deploy para a pagina do git, logo após o Deploy recebo um alert como o erro informando a impossibilidade do fetch.

Menores dificuldades durante a implementação

Acho que tudo foi dificil nesta etapa.

Roger-Melo commented 1 year ago

Olá @EneasFeynlinus!

Passando para dizer que a issue foi visualizada e em até 10 dias úteis a análise será feita =)

EneasFeynlinus commented 1 year ago

Muito obrigado Mestre, fico no aguardo.

Desde já grato.

Enéas da Silva Junior.

MivlaM commented 1 year ago

Olá @EneasFeynlinus

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

Fatoração das funções fazendo o request da cityData e entregando os valores da propriedade Key pra city Weather, assim como as futuras transformações ocorridas na aplicação durante a refatoração. Tambem o uso da data-js para referenciar os elementos e adicionar as informações e as imagens

Se essa etapa te causou essas dúvidas, eu recomendo que revise toda a etapa 11 buscando preencher esses gaps. Ela é muito importante para que você entenda os conceitos de assincronismo que você levará pelo restante de sua carreira de desenvolvedor.

Além disso, recomendo que leia a documentação de introdução à JavaScript assíncrono no MDN:

Asynchronous JavaScript

Não vai ser rápido, mas entender assincronismo é crucial para um Desenvolvedor JS, caso contrário certamente a carreira será prejudicada. E eu não quero isso para você.

Se tiver alguma dúvida, pode abrir outras issues que te ajudo =)

E a uma outra duvida, ao finalizar a aplicação, consigo fazer o request quando executo no meu vsCode. No entanto quando faço o deploy para a pagina do git, logo após o Deploy recebo um alert como o erro informando a impossibilidade do fetch.

No caso você se refere ao netlify né? Acredito que seja isso, pois essa dúvida tem mais relação com o netlify do que com o git ou o github. Bem, a sugestão que posso lhe dar é a seguinte, experimenta colocar um "s" depois de http no começo da string que você está fazendo o request, pois o netlify não permite que requests sejam feitos apenas no protocolo http, você precisa usar o https nesse caso.


Vou deixar abaixo algumas observações sobre sua aplicação.


console.log

O console.log é útil apenas no momento em que você está desenvolvendo.

Sei que vc deve ter usado para visualizar algum valor e esquecido, mas sempre que subir a aplicação para produção ou enviar o código para outra pessoa, prefira removê-los (a não ser que a outra pessoa precise executar aquele console.log também) =)

Para o funcionamento da sua aplicação, não faz sentido que o código tenha essas linhas de console.log como abaixo, a aplicação continua funcionando perfeitamente ao removê-las:

cityForm.addEventListener('submit', async event => {
  // ...
  if (IsDayTime) {
    console.log('It is day') // <---
  } else {
    console.log('It is nuight') // <---
  }
  // ...
  console.log(WeatherText, Temperature.Metric.Value) // <---
})

Nome para o callback do listener de evento

Podemos nomear o callback do listener de evento como showCityWeather, por exemplo. A vantagem aqui, é que nomear callbacks de listeners de eventos dá mais legibilidade ao seu código, o intuito aqui é nomeá-lo descrevendo a ação mais significativa que a função irá executar na aplicação quando for executada, assim ao bater o olho no nome da função, a ação que ela executa fica explícita:

// antes
cityForm.addEventListener('submit', async event => {
  // ...
})
// depois
const showCityWeather = async event => {
  // ...
}
cityForm.addEventListener('submit', showCityWeather)

Bloco if e método contains

Nesse trecho de código, o condicional if não é necessário, porque o d-none só vai estar presente antes de o usuário fazer a primeira busca, portanto, o código pode funcionar da mesma forma apenas com o remove. Você também pode deixar o código mais curto e mais simples de entender ao remover o método contains. Para facilitar o fluxo da leitura, uma sugestão seria fazer com que a remoção do d-none seja a última manipulação de DOM a ser feita na função, por isso, a ideia aqui é deixar esse trecho de código como última linha da função. Veja abaixo como ficaria a refatoração desse trecho de código por completo:

// antes
const showCityWeather = async event => {
  // ...

  if (cityCard.classList.contains('d-none')) {
    cityCard.classList.remove('d-none')
  }

  timeIconContainer.innerHTML = timeIcon
  cityNameContainer.textContent = LocalizedName
  cityWeatherContainer.textContent = WeatherText
  cityTemperatureContainer.textContent = Temperature.Metric.Value
  cityForm.reset()
}
// depois
const showCityWeather = async event => {
  // ...

  cityCard.classList.remove('d-none')
  timeIconContainer.innerHTML = timeIcon
  cityNameContainer.textContent = LocalizedName
  cityWeatherContainer.textContent = WeatherText
  cityTemperatureContainer.textContent = Temperature.Metric.Value
  cityForm.reset()
}

Operador ternário

Se for interessante para você, você pode abstrair as repetições de timeImg.src e de benefício deixá-lo mais simples escrevendo menos código. Uma implementação ideal, seria você remover os blocos do if e usar o operador ternário, isso poderia ser feito ao fazer timeImg.src receber a condição de IsDayTime da seguinte forma:

// antes
if (IsDayTime) {
  timeImg.src = './src/day.svg'
} else {
  timeImg.src = './src/night.svg'  
}
// depois
timeImg.src = IsDayTime ? './src/day.svg' : './src/night.svg'

inputValue

Algo que pode ser útil, seria usar o método trim para remover os espaços em branco de uma eventual busca ao inserir o nome da cidade. Sabendo que o método trim remove os espaços em branco do início e fim de um texto, ele pode ser usado para evitar com que um espaço em branco inserido acidentalmente, por exemplo, prejudique a busca de um nome. Ficaria assim:

// antes
const inputValue = event.target.city.value
// depois
const inputValue = event.target.city.value.trim()

Validação

Na função showCityWeather, algo que você pode adicionar e que seria interessante como funcionalidade para o usuário é inserir uma validação no inputValue. Esse inputValue pode ter uma verificação para caso uma string vazia ou string com menos de três caracteres for enviada como input. Numa situação assim, você pode, se achar que faz sentido, exibir uma mensagem na tela por meio do alert comunicando que é necessário inserir o nome de uma cidade. Ficaria assim:

const showCityWeather = async (event) => {
  event.preventDefault()

  const inputValue = event.target.city.value.trim()

  if (inputValue.length < 3 || inputValue === ''){
    alert("Por favor, insira o nome da cidade")  
    return
  }

  // ...
}

Abstração em showCityWeather

Uma outra possibilidade de refatoração seria, por meio de uma abstração, você não precisar mais usar a invocação de getCityData. Segundo o raciocínio, ao invés de termos essa função, nós vamos ter apenas a função getCityWeather que vai fazer o que a getCityData está fazendo. A ideia dessa refatoração é ao invés de invocarmos duas funções, simplificar as coisas e precisarmos apenas invocar uma função que irá fazer o papel das duas =)

Dito isso, para entender de fato essa abstração que vou mostrar, eu sugiro que você acompanhe o passo a passo abaixo junto comigo, ok?

Vamos lá, vou criar uma função chamada getCityWeather que ao invés de receber a key, vai receber o nome da cidade. Antes de buscarmos o clima da cidade, precisamos obter as informações da cidade para obtermos a key, pois só por meio dela poderemos fazer o segundo request, no caso do seu código, o usado na função fetchData. Para desaninharmos as funções invocadas dentro da fetchData, vamos criar uma const cityInfoURL que vai receber a getCityUrl e vamos exibí-la no console para confirmarmos se os resultados que esperamos estão sendo obtidos.

const getCityWeather = city => {
  const cityInfoURL = getCityUrl(city)
  console.log(cityInfoURL) // Url com o nome da cidade no query parameter aparece...
} 

Não se preocupe se aparecer algum erro no console, o intuito aqui é, ao executarmos um console.log, ver a url com o nome da cidade no query parameter, ou seja, a cidade buscada. Feito isso, já que essa url está sendo obtida, podemos criar uma const cityInfo que vai armazenar o retorno da função fetchData. Seguindo o raciocínio, cityInfo vai ser uma função async, portanto terá um await que espera a resolução dessa promise que contém cityInfoURL como argumento. Ficaria assim:

const getCityWeather = async city => {
  const cityInfoURL = getCityUrl(city)
  const cityInfo = await fetchData(cityInfoURL)
  console.log(cityInfo) // Array de objetos aparece...
} 

Ao visualizarmos isso no console, veremos que o resultado é um array de objetos em que o primeiro objeto vai corresponder, na maioria das vezes, a cidade escolhida no input.

No código que usamos para manipular o DOM, estamos usando, além da propriedade Key, a propriedade LocalizedName. Então, também vamos precisar da propriedade localizedName, e essa função getCityWeather vai ter que retornar ela para quando for invocada ela ser usada no destructuring e e então servir como argumento dessa invocação.

Para fazermos o destructuring no array de objetos e pegarmos do primeiro objeto, as propriedades LocalizedName e Key, vamos fazer um destructuring nesse primeiro objeto, na propriedade Key e na LocalizedName. Feito isso, se exibirmos no console, aparece a Key e o nome da cidade como LocalizedName.

const getCityWeather = async city => {
  const cityInfoURL = getCityUrl(city)
  const [{ Key, LocalizedName }] = await fetchData(cityInfoURL)
  console.log(Key, LocalizedName) // Key e LocalizedName aparecem no console...
} 

Após isso, vamos fazer o segundo request para obtermos a informação do clima dessa cidade, para isso, vamos usar a Key. Para isso, vamos obter a url, vou criar uma const cityWeatherURL recebe a invocação de getCityWeatherUrl() que recebe a Key como argumento. Dessa forma, a url com a Key vai ser retornada corretamente.

const getCityWeather = async city => {
  const cityInfoURL = getCityUrl(city)
  const [{ Key, LocalizedName }] = await fetchData(cityInfoURL)
  const cityWeatherURL = getCityWeatherUrl(Key)
  console.log(cityWeatherURL) // Url com a Key vai ser retornada...
} 

Feito isso, agora que temos a url, falta fazer o último request, que é o request que vai buscar os dados do clima da cidade. Para fazer isso, vamos criar uma const cityWeather que recebe fetchData com await antes porque é uma função assincrona, e esse fetchData vai receber cityWeatherURL como argumento.

const getCityWeather = async city => {
  const cityInfoURL = getCityUrl(city)
  const [{ Key, LocalizedName }] = await fetchData(cityInfoURL)
  const cityWeatherURL = getCityWeatherUrl(Key)
  const cityWeather = await fetchData(cityWeatherURL)
  console.log(cityWeather) // Array com um objeto que contém os dados do clima da cidade.
} 

Agora, podemos retornar cityWeather e para que essa função não retorne um array num objeto, podemos fazer com que ela retorne apenas um objeto por meio de um destructuring.

const getCityWeather = async city => {
  const cityInfoURL = getCityUrl(city)
  const [{ Key, LocalizedName }] = await fetchData(cityInfoURL)
  const cityWeatherURL = getCityWeatherUrl(Key)
  const [cityWeather] = await fetchData(cityWeatherURL)

  return cityWeather
} 

Você se lembra que esse objeto que está sendo retornado por essa função também precisa ter LocalizedName? Então, ao invés de retornar um objeto, você pode retornar um novo objeto que contém as propriedades do objeto cityWeather e também contém LocalizedName. Aqui foi criado um novo objeto que contém as propriedades do objeto cityWeather espalhados pelo spread operator e que também contém a LocalizedName.

const getCityWeather = async city => {
  const cityInfoURL = getCityUrl(city)
  const [{ Key, LocalizedName }] = await fetchData(cityInfoURL)
  const cityWeatherURL = getCityWeatherUrl(Key)
  const [cityWeather] = await fetchData(cityWeatherURL)

  return { ...cityWeather, LocalizedName }
}  

Por fim, resta inserir LocalizedName como um dos destructurings da função getCityWeather. Nessa função, o destructuring do array, ou seja, os colchetes, podem ser removidos pois agora a getCityWeather não está mais retornando um array, mas um objeto. Por isso não é mais necessário os colchetes do destructuring.

const { WeatherText, Temperature, IsDayTime, WeatherIcon, LocalizedName } = await getCityWeather(inputValue)

Após isso, você não vai precisar mais da função getCityData e o código estará funcionando perfeitamente bem =)


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

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

As observações fizeram sentido?

EneasFeynlinus commented 1 year ago

Boa noite @MivlaM tudo certo por ai? Esperus que sim. Muito bacana suas observações e adotei todas. E realmente as vezes esqueço do ternario que torna mais legivel o codigo. E aos poucos espero qeu o desafio da refatoração de um codigo fique menos desafiador, pq o coisa que gera ansiedade uauhahuua. Muito bacana a sacada de transformar os requests em apenas uma função vou adotar essa tambem. Desde já grato. Tudo de bom.

EneasFeynlinus commented 1 year ago

Boas noite @MivlaM apenas mais uma duvida. O método contains, nao teria alguma utilidade nessa aplicação apenas para mostrar seu uso para os entrevistadores? Abraços

EneasFeynlinus commented 1 year ago

Boa noite @MivlaM e so mais uma duvida. Como perguntei anteriormente, tentei novamente fazer o deploy no netlify, no entanto eu recebo esse alert: TypeError: Failed to fetch

Eu fiz o deploy no netlify este é o link : https://appforecast.netlify.app

O curioso é que no visual code na minha maquina consigo fazer o request, no entanto ao fazer o deploy ele falha. Poderia me ajudar com isso? onde estou errando? Abraços

MivlaM commented 1 year ago

Olá @EneasFeynlinus

O método contains, nao teria alguma utilidade nessa aplicação apenas para mostrar seu uso para os entrevistadores?

Eu acredito que numa eventual avaliação de um entrevistador, o mesmo acharia mais interessante o código estar mais compacto e desempenhando a mesma funcionalidade, porém com um menor número de linhas de código, métodos e de forma bem mais legível. De qualquer forma, o log de refatorações fica disponível no github e você também pode, se achar necessário, escrever uma descrição bem detalhada do que se trata o projeto e de suas atualizações conforme você foi avançando na refatoração no treinamento.

Como perguntei anteriormente, tentei novamente fazer o deploy no netlify, no entanto eu recebo esse alert: TypeError: Failed to fetch

Eu acessei o link aqui, e observei o seguinte erro no console:

Errora

--> Mixed Content: The page at 'https://appforecast.netlify.app/' was loaded over HTTPS, but requested an insecure resource 'http://dataservice.accuweather.com/locations/v1/cities/search?apikey=BgtuUrTT2I38R0K9MghVtXnxfs0v3jSe&q=Miami'. This request has been blocked; the content must be served over HTTPS.

Este aviso é uma mensagem de segurança destinada a impedir que um site seguro (https) se torne inseguro (http), proibindo que você carregue conteúdo inseguro por meio de uma chamada de API. Você pode conferir novamente a linha 2 do seu código no weather.js? A const baseUrl precisa estar da seguinte forma:

// antes
const baseUrl = 'http://dataservice.accuweather.com/'
// depois
const baseUrl = 'https://dataservice.accuweather.com/'

Após essa alteração, tente subir novamente essa aplicação para o netlify, sugiro que use o navegador Google Chrome.

Comenta aqui em baixo se funcionou =)

EneasFeynlinus commented 1 year ago

Bom dia @MivlaM caramba cara, que vacilo, eu recebendo esse erro a dias quebrando a cabeça e não sei porque não fui olhar a console pra ver o erro, achando que o erro era no meu deploy. Poxa valew mesmo de me ajudar a encontrar esse erro que não estava vendo, e vc tinha comentado essa parada do https na primeira mensagem sobre a minha aplicação. No entanto, tinha pensado que era alguma opção que tinha que escolher na hora de fazer o deploy escolhendo uma opção segura. Não tinha sacado que era a URL que fazemos o request para a API. Mas ai fica uma duvida, aquela URL sem o S do http eu copiei da propria API, porque será que eles não passam o link já com o https? Teria algum motivo?

E muito obrigado deu super certo aqui @MivlaM obrigado a voce e ao Mestre Roger Melo. Tudo de bom

MivlaM commented 1 year ago

Olá @EneasFeynlinus

Mas ai fica uma duvida, aquela URL sem o S do http eu copiei da propria API, porque será que eles não passam o link já com o https? Teria algum motivo?

Bem, tem alguns motivos mais técnicos:

O protocolo HTTP é o protocolo mais comum usado por APIs para transmitir dados pela Internet. Embora o HTTPS, a versão segura do HTTP, seja o preferido para comunicação segura, o HTTP ainda é comumente nos seguintes casos:

-> o HTTP é um protocolo simples e amplamente suportado que permite fácil implementação e integração com outros sistemas. É um protocolo sem estado, o que significa que cada solicitação é independente das solicitações anteriores, o que o torna ideal para aplicativos baseados na Web que exigem transferência de dados rápida e eficiente.

-> Algumas APIs são projetadas para transmitir dados não confidenciais que não requerem a segurança adicional do HTTPS. Por exemplo, as APIs que fornecem dados públicos, como previsões meteorológicas como a própria accuweather ou artigos de notícias, podem não exigir HTTPS, pois os dados já estão disponíveis publicamente.

-> O HTTPS vem com uma sobrecarga de processamento maior devido à criptografia e depois a descriptografia de dados, o que pode resultar em tempos de resposta mais lentos para APIs de alto tráfego. Isso pode ser uma preocupação para APIs que exigem transferência de dados rápida e eficiente, como dados financeiros em tempo real ou plataformas de jogos online.

No entanto, é importante observar que o HTTPS costuma ser o mais preferido para APIs que transmitem dados confidenciais, como credenciais de usuário, informações de pagamento ou registros de saúde. O HTTPS fornece um canal de comunicação seguro criptografando dados e verificando a identidade do servidor, o que ajuda a evitar espionagem, adulteração de dados e ataques man-in-the-middle.

Por fim, o que aconteceu foi que o netlify sempre exige que use o HTTPS, e como a aplicação estava usando HTTP, aconteceu esse erro, verificar o console.log é sempre muito útil e é algo que o Roger nos ensina a olhar toda hora nas aulas dele hehe.

Mas enfim, embora o HTTP ainda seja comumente usado para comunicação de API devido à ser mais simples, o HTTPS é o protocolo preferido para transmissão de dados confidenciais pela Internet.

Consegui responder a dúvida? Se ficou confuso, sinta-se livre em deixar mais um comentário aqui que eu vou te ajudar! =)

EneasFeynlinus commented 1 year ago

Conseguiu totalmente, realmente faz sentido a API da accuWeather ser http, já que como vc respondeu são dados publicos e não confidenciais. E como vc também comentou que achei muito interessante a questão dos millisegundos tomados ao criptografar e descriptografar os dados. Dependendo do uso que esta aplicação esteja sendo feita e dos dados utilizados, o HTTPS somente tomarias milisegundos preciosos de uma aplicação e como exemplo com Previsão de tempo, na aviação cada millisegundo é precisos e pode ser fatal. Muito obrigado por sua explicação @MivlaM fez todo sentido. Sem duvidas no momento. Tudo de bom e mais uma vez muito obrigado pela excelente resposta.

MivlaM commented 1 year ago

Ok! Vou fechar a issue aqui, qualquer coisa, sinta-se livre para abrir uma nova.