もりけん塾 JavaScript課題15 モーダルにformを置いてリクエスト送信

もりけん塾 JavaScript課題15 モーダルにformを置いてリクエスト送信

前回はこちら

お題

モーダル内に formをおいて、input(type number)値、input(type text)、を新たに作ったsubmitボタン押下で リクエスト、 APIから値を取ってきてください

最初にプルリクエストしたコード

バリデーションは、HTMLにエラーメッセージ欄を置いておき、空欄だったらエラー文それぞれを表示して、リクエスト送信までいかないようにしています。

<!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>JavaScript課題15</title>
  </head>
  <body>
    <ul id="js-list" class="list"></ul>
    <button type="button" id="js-open-btn" class="btn open-btn">Click</button>
    <div id="js-modal" class="modal">
      <div class="modal-content">
          <div class="close-btn" id="js-close-btn">
            <img src="img/xmark-solid.svg" alt="クローズボタン">
          </div>
          <form class="form">
            <p class="form-info">数字、名前を入力してください</p>
            <p id="js-error-msg"></p>
            <label for="js-input-number">数字</label>
            <input type="number" id="js-input-number" value="" placeholder="100" name="number">
            <label for="js-input-name">名前</label>
            <input type="text" id="js-input-name" value="" placeholder="山田" name="name">
            <input type="submit" id="js-submit-btn" class="btn" value="送信">
          </form>
      </div>
    </div>
  </body>
</html>
const showLoading = () => {
    const loadingPlace = document.createElement('div');
    const loadingImg   = document.createElement('img');
    loadingPlace.id        = "js-loading";
    loadingPlace.className = "loading";
    loadingImg.src = "img/loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.body.insertAdjacentElement('afterbegin',loadingPlace).appendChild(loadingImg);
}

const removeLoading = () => document.getElementById('js-loading').remove();

const renderList = (result) => {
    const fragment = document.createDocumentFragment();
    for(const list of result){
        const li  = document.createElement('li');
        const a   = document.createElement('a');
        const img = document.createElement('img');
        li.className  = "list-item";
        a.href        = `/${list.a}`;
        a.textContent = list.text;
        img.src       = list.img;
        img.alt       = list.alt;
        fragment.appendChild(li).appendChild(a).insertAdjacentElement('afterbegin',img);
    } 
    document.getElementById('js-list').appendChild(fragment);
}

const fetchData = async () => {
    try{ 
    const endpoint = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
    const response = await fetch(endpoint);
    if(!response.ok){
        console.error(`${response.status}:${response.statusText}`)
    }
    const json = await response.json();
    const data = await json.data;
    return data;
    }catch(error){
        console.error(error);
    }
}

const init = async (number,name) => {
    showLoading();
    try{
        console.log(number,name);
        const result = await fetchData();
        if(!result){
            return;
        }
        renderList(result);
    }catch(error){
        console.error(error);
    }finally{
        removeLoading();
    }
}

const modal     = document.getElementById('js-modal');
const openBtn   = document.getElementById('js-open-btn');
const closeBtn  = document.getElementById('js-close-btn');
const submitBtn = document.getElementById('js-submit-btn');

const closeModal  = () => modal.classList.remove('is-show');
const openModal   = () => modal.classList.add('is-show');
const hideOpenBtn = () => openBtn.classList.add('is-hide');
const showOpenBtn = () => openBtn.classList.remove('is-hide');

openBtn.addEventListener('click',() => {
    openModal();
    hideOpenBtn();
}, false);

closeBtn.addEventListener('click',() => {
    closeModal();
    showOpenBtn();
}, false);

submitBtn.addEventListener('click',(event) => {
    event.preventDefault();

    const number  = document.getElementById('js-input-number').value;
    const name    = document.getElementById('js-input-name').value;
    const errMsg  = document.getElementById('js-error-msg');

    /*バリデーション*/
    const errItem = [];
    if(!number){
        errItem.push('数字');
    }
    if(!name){
        errItem.push('名前');
    }
    if(errItem.length){
        errMsg.className   = "error-msg";
        errMsg.textContent = `${errItem}が未入力です`;
        return;
    }

    init(number,name);
    closeModal();
}, false)

バリデーション部分の実装で、空白やスペースであっても文字としてカウントされてしまうため、明確にした方が良いというコメントをもらいました。

空白文字の判定処理には、正規表現を使う方法など多数ありますが、今回は、trim()を使用して、入力文字の前後のスペースを削除する処理を入れることにしました。

    /*バリデーション*/
    const errItem = [];
    if(!number){
    if(!number.trim()){ 
        errItem.push('数字');
    }
    if(!name){
    if(!name.trim()){
        errItem.push('名前');
    }
    if(errItem.length){
        errMsg.className   = "error-msg";
        errMsg.textContent = `${errItem}が未入力です`;
        errMsg.textContent = `${errItem}を入力してください`;
        return;
    }

最終的なコード

<!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>JavaScript課題15</title>
  </head>
  <body>
    <ul id="js-list" class="list"></ul>
    <button type="button" id="js-open-btn" class="btn open-btn">Click</button>
    <div id="js-modal" class="modal">
      <div class="modal-content">
          <div class="close-btn" id="js-close-btn">
            <img src="img/xmark-solid.svg" alt="クローズボタン">
          </div>
          <form class="form">
            <p class="form-info">数字、名前を入力してください</p>
            <p id="js-error-msg"></p>
            <label for="js-input-number">数字</label>
            <input type="number" id="js-input-number" value="" placeholder="100" name="number">
            <label for="js-input-name">名前</label>
            <input type="text" id="js-input-name" value="" placeholder="山田" name="name">
            <input type="submit" id="js-submit-btn" class="btn" value="送信">
          </form>
      </div>
    </div>
  </body>
</html>
const showLoading = () => {
    const loadingPlace = document.createElement('div');
    const loadingImg   = document.createElement('img');
    loadingPlace.id        = "js-loading";
    loadingPlace.className = "loading";
    loadingImg.src = "img/loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.body.insertAdjacentElement('afterbegin',loadingPlace).appendChild(loadingImg);
}

const removeLoading = () => document.getElementById('js-loading').remove();

const renderList = (result) => {
    const fragment = document.createDocumentFragment();
    for(const list of result){
        const li  = document.createElement('li');
        const a   = document.createElement('a');
        const img = document.createElement('img');
        li.className  = "list-item";
        a.href        = `/${list.a}`;
        a.textContent = list.text;
        img.src       = list.img;
        img.alt       = list.alt;
        fragment.appendChild(li).appendChild(a).insertAdjacentElement('afterbegin',img);
    } 
    document.getElementById('js-list').appendChild(fragment);
}

const fetchData = async () => {
    try{ 
    const endpoint = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
    const response = await fetch(endpoint);
    if(!response.ok){
        console.error(`${response.status}:${response.statusText}`)
    }
    const json = await response.json();
    const data = await json.data;
    return data;
    }catch(error){
        console.error(error);
    }
}

const init = async (number,name) => {
    showLoading();
    try{
        console.log(number,name);
        const result = await fetchData();
        if(!result){
            return;
        }
        renderList(result);
    }catch(error){
        console.error(error);
    }finally{
        removeLoading();
    }
}

const modal     = document.getElementById('js-modal');
const openBtn   = document.getElementById('js-open-btn');
const closeBtn  = document.getElementById('js-close-btn');
const submitBtn = document.getElementById('js-submit-btn');

const closeModal  = () => modal.classList.remove('is-show');
const openModal   = () => modal.classList.add('is-show');
const hideOpenBtn = () => openBtn.classList.add('is-hide');
const showOpenBtn = () => openBtn.classList.remove('is-hide');

openBtn.addEventListener('click',() => {
    openModal();
    hideOpenBtn();
}, false);

closeBtn.addEventListener('click',() => {
    closeModal();
    showOpenBtn();
}, false);

submitBtn.addEventListener('click',(event) => {
    event.preventDefault();

    const number  = document.getElementById('js-input-number').value;
    const name    = document.getElementById('js-input-name').value;
    const errMsg  = document.getElementById('js-error-msg');

    /*バリデーション*/
    const errItem = [];
    if(!number.trim()){
        errItem.push('数字');
    }
    if(!name.trim()){
        errItem.push('名前');
    }
    if(errItem.length){
        errMsg.className   = "error-msg";
        errMsg.textContent = `${errItem}を入力してください`;
        return;
    }

    init(number,name);
    closeModal();
}, false);

今回学んだこと

  • trim()関数の使い方
  • バリデーションのやり方

次は、タブ切り替えの実装です!難しそうなので頑張ります。

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

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

https://kenjimorita.jp/

まい

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

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