Objeto: fecth, Objetos JavaScript, formato JSON
Principais termos técnicos abordados: JSON, fetch, await, html, JavaScript, arrow function
Requisitos para as instruções funcionarem: editor de texto e navegador instalados
Requisitos para compreensão das instruções: noções bem básicas de HTML e ter implementado receita anterior
Como ler essa receita: instruções, dentro dos passos da receita e que requerem ação sua no computador, estarão escritas em cor azul. Comentários sobre os passos estarão em fonte normal, cor preta. Comandos, código-fonte, termos técnicos ou configuração explícita, estarão com fonte diferenciada.
De alguns textos anteriores pra cá, a fonte de informações de nossas páginas está na própria página, num vetor de valores fixos. No mundo da programação, dizemos que essas informações estão hardcoded.
Basicamente, a gente tem um array ao estilo...
let cervejas = [
{
name: "Founders Breakfast Stout",
alcohol: "7.6%"
},
{
name: "Celebrator Doppelbock",
alcohol: "3.1%"
},
{
name: "Dreadnaught",
alcohol: "6.1%"
}
]
Esse não é um cenário muito realista. A gente pode pensar: "bem, se as informações da página estão fixas, pra que djabo eu tô escrevendo scripts? Melhor seria fazer diretamente a página html com as informações fixas e não um código JavaScript que gera a página".
Essa reflexão faz todo sentido. No entanto, agora, seguindo todos os textos até aqui, a gente já sabe processar um vetor de objetos JSON e transformá-los em interface com o usuário. Se a gente descobrisse um jeito de ir buscar esse vetor de objetos em outro canto que não seja uma variável fixa em nosso próprio código, nossa página ficaria bem mais interessante, não é?
É a isso que nos dedicaremos nesta receita.
Vamos partir de um código familiar.
Edite e salve um arquivo com o seguinte conteúdo.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [
{
name: "Founders Breakfast Stout",
alcohol: "7.6%"
},
{
name: "Celebrator Doppelbock",
alcohol: "3.1%"
},
{
name: "Dreadnaught",
alcohol: "6.1%"
}
]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
Carregue a página, acione o botão que aparece nela para familiarizar-se com a dinâmica do script. Se não entender nada desse código, volte para receita anterior.
Primeiramente, vamos definir a nossa fonte de informações. Ela será uma API online.
Você pode entender uma API online como um site na internet feito para ser acessado por programas de computador e não por pessoas. Os sites acessados por pessoas respondem às requisições com texto no formato HTML, ao passo que essas tais APIS, os sites para serem acessados por programas de computador, respondem com texto no formato JSON. Essa é uma visão bem simplificada da coisa toda, claro, mas vai servir ao propósito de entender o assunto.
Vamos testar a tal API.
Se é um site na internet, a gente pode acessar pelo navegador, né?
Então vamos lá.
Acesse, no navegador, o endereço https://random-data-api.com/api/v2/beers?size=3
O navegado deve mostrar algo parecido com...
[
{
"id": 7612,
"uid": "ac23da70-f734-4c08-a6c2-79b998e12df6",
"brand": "Lowenbrau",
"name": "90 Minute IPA",
"style": "Porter",
"hop": "Newport",
"yeast": "1318 - London Ale III",
"malts": "Caramel",
"ibu": "67 IBU",
"alcohol": "5.4%",
"blg": "13.7°Blg"
},
{
"id": 8937,
"uid": "e462ecb0-fe88-44ae-8113-90ca4a4af956",
"brand": "Paulaner",
"name": "Ruination IPA",
"style": "European Amber Lager",
"hop": "Millennium",
"yeast": "3711 - French Saison",
"malts": "Pale",
"ibu": "31 IBU",
"alcohol": "8.3%",
"blg": "11.6°Blg"
},
{
"id": 5559,
"uid": "49585d87-0344-4c73-b1fa-2ea037ed984f",
"brand": "Carlsberg",
"name": "Brooklyn Black",
"style": "German Wheat And Rye Beer",
"hop": "Brewer’s Gold",
"yeast": "2001 - Urquell Lager",
"malts": "Munich",
"ibu": "18 IBU",
"alcohol": "10.0%",
"blg": "9.7°Blg"
}
]
Ora, ora, mas isso é muito parecido com aquele vetor cervejas do nosso script... inclusive as propriedades name e alcohol estão lá.
- Professor, essa resposta que o navegador exibiu são objetos JSON???
Excelente pergunta, mas a resposta é não. Isso é um texto, um string, se você preferir. Mas é um texto que segue as regras de formatação do JSON, então não deve ser a coisa mais difícil do mundo converter esse stringão num vetor de objetos JSON para nosso script manipular.
A ideia da gente é fazer, através de nosso script JS, essa requisição que a gente fez pelo navegador, em seguida converter esse string em objetos JSON, empurrar o resultado da conversão no vetor cervejas e, aí sim, montar a interface com o usuário através da função que a gente já tem.
A gente pode fazer a requisição através de nosso script. Não é tão difícil assim. Primeiro vamos fazer no Inspetor de código, depois vamos fazer no nosso script.
Abra a página no navegador e abra o Inspetor de código, no Console.
No console, escreva o comando:
let res = await fetch("https://random-data-api.com/api/v2/beers?size=3")
Agora escreva
res
A resposta deve ser algo como...
Note primeiro que a gente precisou escrever um await antes do fetch. Eu volto ao await daqui a pouco.
O mais importante agora é que a função fetch NÃO retornou o texto JSON que a gente esperava.
De fato, o fetch retorna um objeto do tipo Response. É um objeto que representa, nesse caso, uma resposta a uma requisição HTTPS, que foi o que a gente fez. Esse objeto não é o nosso texto JSON, mas no meio dessa resposta está o nosso texto JSON. O texto JSON é o corpo da resposta, é o que o navegador nos exibe quando fazemos a requisição por ele.
Entretanto a gente não quer lidar com um string JSON, não é mesmo? A gente quer transformar esse string que segue o formato JSON em objetos JSON, porque com os objetos JSON a gente já aprendeu a lidar.
Felizmente o objeto Response tem um método que é capaz de converter o corpo da resposta em objetos JSON. Assim fica fácil?
Escreva...
let objetos = await res.json()
Em seguida, escreva
objetos
Opa, agora temos algo que conhecemos...
Ou seja, temos um vetor de objetos. Mais precisamente, um vetor de objetos com o qual nossa função carregarDiv já sabe lidar.
Agora precisamos incorporar esse código ao nosso script.
E vai ser mais fácil que ter raiva de flamenguista, porque o trabalho sujo vai ser feito por funções que já existem, a função fetch() e a função json().
- Mas e aquela fuleiragem de await, professor?
Calma que o Brasil é nosso.
A gente, tecnicamente, só precisaria reescrever um código como o que se segue.
Tecnicamente.
Salve e recarregue seu arquivo com o seguinte conteúdo.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = []
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
function carregarCervejas(){
let res = await fetch("https://random-data-api.com/api/v2/beers?size=3")
cervejas = await res.json()
carregarDiv(cervejas)
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarCervejas() )
</script>
</html>
Se você continuou no Console deve ter encontrado o erro:
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
Isso basicamente quer dizer que você só pode usar o construto await em funções async. É literalmente o que a mensagem diz.
Então não pense muito e torne async a função onde você usa o await.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = []
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
async function carregarCervejas(){
let res = await fetch("https://random-data-api.com/api/v2/beers?size=3")
cervejas = await res.json()
carregarDiv(cervejas)
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarCervejas() )
</script>
</html>
E pronto, o código funciona que é uma beleza...
E melhor ainda: a cada clique no botão ele exibe cervejas diferentes. É que essa API funciona assim, manda respostas aleatórias a cada requisição, daí o nome random na URL da API.
- sim, mas agora apareceu um async também...
Bem, aquele await a gente tem que usar porque o fetch é uma função que faz processamento assíncrono. Ela retorna imediatamente um objeto SEM o valor de resposta da requisição, um objeto que vai deixar a consulta moendo por debaixo dos panos, um objeto que a gente pode usar para receber a resposta da requisição quando ela vier usando uma função de callback.
Sendo que a gente não tem nada melhor pra fazer enquanto essa resposta não chega, e nesses casos a gente pode usar o await. Essencialmente, o await está dizendo pro navegador que executa teu código esperar até a resposta da requisição do fetch() efetivamente retornar para poder continuar a execução. O método res.json() também faz processamento assíncrono, também retorna imediatamente a tal Promessa, e em vez da gente lidar com essa promessa a gente pode usar o await para que a função só retorne quando efetivamente a conversão tiver sido realizada.
A gente vai ter que estudar promessas mais adiante, claro. Mas por enquanto é mais simples de entender usando o await. Com o async, no caso, a gente apenas está sinalizando que dentro daquela função há processamento assíncrono em algum momento.
Note que esse processo pode receber como resultado uma resposta vazia. Seu script deve estar preparado para lidar com isso, fazendo um if em algum canto e exibindo uma mensagem adequada em vez de simplesmente não exibir nada.
As chamadas às funções de biblioteca podem dar erro também. Sua URL pode estar com o endereço errado - o fetch() dará erro - ou a resposta para a requisição pode não seguir o padrão JSON porque você requisitou uma página HTML, por exemplo - o json() dará erro, nesse caso.
Tem como contornar isso com um bloco try/catch.
Aqui vamos com uma solução chinfrim apenas para ilustrar o uso.
Use um bloco try/catch para tratar situações excepcionais...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = []
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
async function carregarCervejas(){
try{
let res = await fetch("https://random-data-ai.com/api/v2/beers?size=3")
cervejas = await res.json()
carregarDiv(cervejas)
}catch(err){
document.getElementById("cervejasDiv").innerHTML = "Fudeu..."
}
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarCervejas() )
</script>
</html>
Note que a URL foi alterada para forçar o erro, e que a mensagem será exibida pois o fetch() lançará exceção.
E por hoje é só, crianças.
Exiba outras propriedades das cervejas em sua pájina.
Bonitifique sua página.
Procure nessa API outros itens diferentes de cerveja e adapte seu código para exibir propriedades desses itens
Faça uma função genérica para montar uma tabela de qualquer coisa, ponha essa função num arquivo .js separado, importe essa função para sua página e use-a para exibir uma tabela de qualquer outra coisa que não seja cerveja. Use essa API do random-api.