もりけん塾 JavaScript課題11 APIから値を取得する

もりけん塾 JavaScript課題11 APIから値を取得する

課題11は、Fetchを使って、APIから値を取得、取得した値を使ってDOMを表示する課題です。ちょっと難しそうですが、外部にあるデータをとってくる、ということを初めて行うので、ワクワクが大きかったです。とても、楽しんで実装することができました。

お題

簡易的なAPIを使って同じことをこちらのサイト、my json

{ "data": [
  {
    "a": "bookmark",
    "img": "img/1.png",
    "alt": "画像1",
    "text": "ブックマーク"
  },
  {
    "a": "message",
    "img": "img/2.png",
    "alt": "画像2",
    "text": "メッセージ"
  }
]}

上記を登録してエンドポイントを取得してください。

前回までのコードを生かして fetchを使ってデータを取得してください。取得したデータは前回と同じように表示してください。

最初のコード

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="reset.css" type="text/css" />
    <link rel="stylesheet" href="style.css" type="text/css" />
    <title>TerraceTechフロントエンドエンジニア養成所</title>
  </head>
  <body>
    <div id="js-loading">
    </div>
    <div>
      <ul id="js-list"></ul>
    </div>
    <script src="script.js"></script>
  </body>
</html>
function showLoadingImg(){
    const loadingImg = document.createElement('img');
    loadingImg.src = "loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.getElementById('js-loading').appendChild(loadingImg);
}

function removeLoadingImg(){
    document.getElementById('js-loading').remove();
}

async function getData(){
    showLoadingImg();
    try{
        const url = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
        const response = await fetch(url);
        const json = await response.json();
        const data = await json.data;
        return data;
    }catch(e){
        document.getElementById('js-list').textContent = "データが取得できませんでした";
        throw new Error(e);
    }finally{
        removeLoadingImg();
    }
}

async function  showList(){
    const result = await getData();
    const fragment = document.createDocumentFragment();
    const ul = document.getElementById('js-list');

    if(result){
        for(const list of result){
            const li = document.createElement('li');
            const a = document.createElement('a');
            const img = document.createElement('img');
            a.textContent = list.text;
            a.href = `/${list.to}`;
            img.src = list.img;
            img.alt = list.alt;
            fragment.appendChild(li).appendChild(a).appendChild(img);
        }
        ul.appendChild(fragment);
    }else{
        ul.textContent = "条件に一致するデータがありません";
    }
}

showList();

今回は、もなかさんはるさんにコードレビューして頂きました!ありがとうございます。

try-catch-finallyを使ってエラーハンドリングをしていますが、catch節でエラーをスローしているのは何故か、質問されました。

async function getData(){
    showLoadingImg();
    try{
        const url = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
        const response = await fetch(url);
        const json = await response.json();
        const data = await json.data;
        return data;
    }catch(e){
        document.getElementById('js-list').textContent = "データが取得できませんでした";
        throw new Error(e);
    }finally{
        removeLoadingImg();
    }
}

APIに接続できなかったら、showlistの中身の処理もストップさせたかったので、エラーをスローするようにしたのですが、上記のコードだと、エラーをスローしたままでキャッチする箇所がないため、それはあまり良い方法ではない、ということを教えて頂きました。

async function  showList(){
    const result = await getData();
    const fragment = document.createDocumentFragment();
    const ul = document.getElementById('js-list');

    if(result){
        for(const list of result){
            const li = document.createElement('li');
            const a = document.createElement('a');
            const img = document.createElement('img');
            a.textContent = list.text;
            a.href = `/${list.to}`;
            img.src = list.img;
            img.alt = list.alt;
            fragment.appendChild(li).appendChild(a).appendChild(img);
        }
        ul.appendChild(fragment);
    }else{
        ul.textContent = "条件に一致するデータがありません";
    }
}

tryの中で例外処理をして、下記のように書き換えるとスローしたエラーをキャッチできるということを教えてもらいました。

また、if文を使う場合、私は、条件がtrueのときはif(result)の式で書いていたのですが、if(!result)というように書くことで、〜ではないときをtrueにすることができるということを教えて頂きました。

この書き方にすると、早めにリターン文を書くことができるため、コードが効率的になるようです。

async function getData() {
  showLoadingImg();
  try {
    const url = "https://ほげほげ";
    const response = await fetch(url);
    const json = await response.json();
    const data = await json.data;
    //もしレスポンスが.okであれば 定数dataを返す
    if (response.ok) {
      return data;
    }
    //そうでなければ例外処理実行
    throw new Error(`${response.status}:${response.text}`);
  } catch (e) {
    //投げられたエラーはここでキャッチできます。
    document.getElementById("js-list").textContent = e;
    console.error(e);
  } finally {
    removeLoadingImg();
  }
}

async function showList() {
  const result = await getData();
  //resultがtrueでなければなにもしない処理に変更しました。
  if (!result) {
    return;
  }
  const fragment = document.createDocumentFragment();
  const ul = document.getElementById("js-list");
  for (const list of result) {
    const li = document.createElement("li");
    const a = document.createElement("a");
    const img = document.createElement("img");
    a.textContent = list.text;
    a.href = `/${list.to}`;
    img.src = list.img;
    img.alt = list.alt;
    fragment.appendChild(li).appendChild(a).appendChild(img);
  }
  ul.appendChild(fragment);
}

はるさんに、JavaScriptの読み込みを、head内に記述してdeferを使う方法を教えて頂きました。deferを使うと、ブラウザにスクリプト読み込みを待たないように伝えることができます。スクリプトは “バックグラウンド” で読み込みされ、DOM が完全に構築されたときに実行することができます。非同期処理をするときなど、とても便利ですね!

  <head>
    <link rel="stylesheet" href="reset.css" type="text/css" />
    <link rel="stylesheet" href="style.css" type="text/css" />
    <script src="script.js" defer></script>
    <title>TerraceTechフロントエンドエンジニア養成所</title>
  </head>

最終的なコード

関数名の変更などもご指摘いただき、最終的にApproveいただいたコードはこちらです。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="reset.css" type="text/css" />
    <link rel="stylesheet" href="style.css" type="text/css" />
    <script src="script.js" defer></script>
    <title>TerraceTechフロントエンドエンジニア養成所</title>
  </head>
  <body>
    <div id="js-loading">
    </div>
    <div>
      <ul id="js-list"></ul>
    </div>
  </body>
</html>
function showLoadingImg(){
    const loadingImg = document.createElement('img');
    loadingImg.src = "loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.getElementById('js-loading').appendChild(loadingImg);
}

function removeLoadingImg(){
    document.getElementById('js-loading').remove();
}

async function getData(){
    showLoadingImg();
    try{
        const url = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
        const response = await fetch(url);
        //responseがokではないとき
        if(!response.ok){
        throw new Error(`${response.status}:${response.text}`);
        }
        //responseがokのとき
        const json = await response.json();
        const data = await json.data;
        return data;
    }catch(e){
        document.getElementById('js-list').textContent = e;
        console.error(e);
    }finally{
        removeLoadingImg();
    }
}

async function  renderList(){
    const result = await getData();
    //resultがtrueではないとき
    if (!result) {
        return;
    }
    //resultがtrueのとき
    const fragment = document.createDocumentFragment();
    const ul = document.getElementById('js-list');
        for(const list of result){
            const li = document.createElement('li');
            const a = document.createElement('a');
            const img = document.createElement('img');
            a.textContent = list.text;
            a.href = `/${list.to}`;
            img.src = list.img;
            img.alt = list.alt;
            fragment.appendChild(li).appendChild(a).appendChild(img);

今回学んだこと

  • Fetchリクエストの実装方法
  • try-catch-finallyでの例外処理の方法
  • deferについて

deferについて参考にした記事:

https://ja.javascript.info/script-async-defer#ref-691

Fecth APIについて:

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch

私が所属しているフロントエンドエンジニアを目指す方のための塾 「もりけん塾」の森田賢二先生のTwitterはこちら!

先生のブログ「武骨日記」はこちら!

https://kenjimorita.jp/

まい

Webサービス制作会社で、Wordpressのテーマ開発や2Dシミュレーターの開発、JavaScriptを使用したフロントエンド周りの実装を担当しています。 JavaScriptが好きです。 最近は、3D Model Configuratorの制作にチャレンジしています。

もりけん塾カテゴリの最新記事