素のJavascript(Vanilla)だけでフロントエンド開発(05:API+値セット編)

このページでは、ReactやVueではなく、Vanillaと呼ばれる素のJavascriptのみで、フロントエンド開発が可能であるか?を解説します。今回はAPIを利用してINPUT/TEXTAREA/SELECTタグやidに値をセットします。

今回は、前回…

の続きになります。

目次

今回の課題のゴールは?

前回はSELECTのOPTIONタグを追加しましたが、今回はそのOPTIONの値をセットします。

併せて、INPUTタグ(チェックボックス、ラジオボタン含む)や、TEXTAREAにも値をセットします。

さらにidタグの中にもテキストを挿入します。

INPUT/TEXTAREA/SELECTタグのname属性について

ちょっと、その前に…

ここ数年システム開発で気がついたのですが、INPUTタグ等の入力項目にname属性が付いていないケースを見かけることがあります。
以下の理由でしょうか…?

  • 何かにつけidを付けたがるので、いつのまにかidがnameの代わりになっていて、nameの必要性がわからない?
  • JSONを使用したAPIのみで完結するフロントエンドシステムを開発する際は、name属性をつける必要性がない?
  • そもそも、Webアプリのチュートリアル等が、フロントエンドに偏り過ぎで、バックエンドのことをほとんど考えていない?

ということで、name属性の意味をざっくりと説明しておきます。

  • サブミット(画面遷移)した際に、name属性はPOST/GETのパラメータ名として、サブミット先に渡る
  • fetch()をフォームデータで実行した際にも、nameがパラメータ名がAPIに渡る

今回はAPIに値を飛ばすのではなく、APIから値を受け取るパターンなので、この段階ではまだ不要ではありますが、今のうちにname属性に慣れておきましょう…ということで、入力項目に値をセットする際にはidは極力使用しないようにします
(idはSPANタグ内に値をセットする程度。)

HTMLに値を埋め込む

プログラム例

今回のソースは、前回のソースをアレンジしたものになります。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="js/vanilla-front-end.js"></script>
<script>
async function setInitData() {

  // SELECTのOPTIONを作成
  // API実行
  let result = await fetch("api_mock/select.json", {
      method: "GET"
  });
  // JSONを取り出す
  let jsonData = await result.json();
  //console.log(jsonData.result);
  // selectのoptionを追加する
  addSelectOption(jsonData.result, true);

  // INPUTタグの値をセット
  // API実行
  result = await fetch("api_mock/get_input.json", {
      method: "GET"
  });
  // JSONを取り出す
  jsonData = await result.json();
  //console.log(jsonData.result);
  // 入力値をセットする
  setInputValue(jsonData.result, 'frm');

} // function

// DOMコンテンツのロード完了時に実行
window.addEventListener('DOMContentLoaded', (ex) => {
  // APIを使用した初期表示
  setInitData();
});
</script>
</head>
<body>

<form name="frm" id="FORM_AREA">
開始時間:<select name="hoge1" class="sel_time_kbn"></select>
<input name="str_time" value="">
<br />
終了時間:<select name="hoge2" class="sel_time_kbn"></select>
<input name="end_time" value="">
 実績時間:<span id="calc_time"></span>
<br />
<br />
元号1:<select name="foo1" class="sel_gengo"></select><br />
元号2:<select name="foo2" class="sel_gengo">
  <option value="X">ここには追加しません</option>
  <option value="M">明治</option>
  <option value="T">大正</option>
</select>
<br />
<br />
外出:<input type="radio" name="gaishutsu" value="1">あり
<input type="radio" name="gaishutsu" value="0" checked>なし
<br />
勤怠:<input type="checkbox" name="kintai" value="1">遅刻
<input type="checkbox" name="kintai" value="2">早退
<input type="checkbox" name="kintai" value="3">電車遅延
<br />
メモ:<textarea name="memo" rows="5" cols="30"></textarea>
</form>

<template id="bar">
  テンプレート内の選択項目<select name="hoge3" class="sel_time_kbn"></select>
  テンプレート内のINPUT<input name="bar1" value="">
</template>

</body>
</html>
function setInputValue(jr, frm_name) {

  jr.forEach(function (inp) {

    // INPUTタグのname属性に一致したelementを取得
    // チェックボックスやラジオボタンは配列にならず、1件にまとまる
    // なので、CSSセレクタのほうが確実にtypeが取得できる
    let elm = document.querySelector('[name="' + frm_name + '"]' + ' [name="' + inp.name + '"]');
    // 存在する場合
    if (elm) {
      //console.log('=====');
      //console.log(elm.tagName);
      //console.log(elm.attributes);
      //console.log(elm.type);
      //console.log(elm.length);
      // SELECTタグの場合
      if (elm.tagName == 'SELECT') {
          elm.value = inp.value;
        return; // このreturnはsetInputValueのリターンではなくcontinueと同じ意味
      }

      // INPUTタグ(ラジオボタン、チェックボックス)
      if (elm.tagName == 'INPUT' && (elm.type == 'radio' || elm.type == 'checkbox')) {
        // フォームデータを取り直す
        let elm2 = document.forms[frm_name].elements[inp.name];
        let arr = inp.value.split(',');
        elm2.forEach(function (el2) {
          el2.checked = (arr.indexOf(el2.value) > -1) ? true : false;
        }); // elm2.forEach(function (inp) {
        return; // このreturnはsetInputValueのリターンではなくcontinueと同じ意味
      }
  
      // INPUTタグあるいはTEXTAREA
      if (elm.tagName == 'INPUT' || elm.tagName == 'TEXTAREA') {
          elm.value = inp.value;
        return; // このreturnはsetInputValueValueのリターンではなくcontinueと同じ意味
      }
    } // if (elm) {

    // idに一致したelementを取得
    elm = document.getElementById(inp.name);
    // 存在する場合
    if (elm) {
      //elm.innerHTML = inp.value;
      elm.innerText = inp.value;
      return; // このreturnはsetInputValueのリターンではなくcontinueと同じ意味
    } // if (elm) {

  }); // jr.forEach(function (inp) {
  
} // function

デバッグ用のconsoleは残しておきましたので、中を覗いてみてください。

addSelectOption()vanilla-front-end.jsにない場合は、前回の内容をコピペしてください。

さらに、前回同様、api_mockディレクトリを作成し、前回作成したselect.jsonをコピーしてください。
同ディレクトリにはget_input.jsonを作成し、以下のソースを挿入してください。

{
  "result" : [
    {"name" : "hoge1", "value" : "AM"},
    {"name" : "hoge2", "value" : "PM"},
    {"name" : "foo1", "value" : "H"},
    {"name" : "foo2", "value" : "T"},
    {"name" : "str_time", "value" : "09:00"},
    {"name" : "end_time", "value" : "17:30"},
    {"name" : "calc_time", "value" : "07:30"},
    {"name" : "gaishutsu", "value" : "1"},
    {"name" : "kintai", "value" : "1,3"},
    {"name" : "bar1", "value" : "セットされないはず"},
    {"name" : "memo", "value" : "あああ\nいいい\nううう"}
  ]
}

実行方法

http://localhost/~(ユーザー名)/test_api_input.html
で、

  • SELECTのOPTIONの値が選択されている
  • INPUTの値が反映されている
  • チェックボックスやラジオボタンの値が設定されている
  • 入力項目以外の欄にも値がセットされている

であればOKです。

setInputValue()の処理手順

setInputValue()の処理手順は下記の通り。

  1. INPUTタグのNAMEと一致するelementsをquerySelector()によって取得
  2. elements存在する場合
    • SELECTタグの場合はvalue値をセットする
    • ラジオボタン、チェックボックスの場合は、再度elementsを取り直し、APIのvalue値のリスト(カンマ区切り)の中で一致するものが合った場合、checkedをtrueにする
      (複数選択に対応)
    • その他のINPUTタグ(textの他にもdateやtimeなど)やTEXTAREAタグの場合は、valueに値をセットする
  3. elements存在しない場合、idでelementsを取得し、存在する場合はinnerTextに値をセットする

以上です。

TEXTAREAの改行について

TEXTAREAの改行コードについては、JSON上で”\n”(\はバックスラッシュ)としておくと、Javascript⇒HTMLの段階で自動的に改行コードに置き換えてくれるようですね。

querySelector()とforms[].elements[]の挙動の違い

「ラジオボタン、チェックボックスの場合は、再度elementsを取り直し」とありますが、querySelector()と、forms[].elements[]やquerySelectorAll()の挙動の違いは下記のとおりです。

elements取得方法配列radio/checkbox.tagName.length.type
querySelector()常に1件INPUT✕(undefined)○(radio/checkbox)
forms[].elements[]△※選択数に一致undefined○(件数)
querySelectorAll()常に選択数に一致undefined○(件数)

※forms[].elements[]は、radio/checkboxの場合のみ配列になります。

querySelector()は、radio/checkboxでも配列にはならず、同一nameを1つにまとめ、ついでにtype属性も取得できるので、好みもあり、使用しています。
しかし、radio/checkboxの値をセットする際に、再度forms[].elements[]を取り直してループする必要があります

お気に召さない場合は、forms[‘hoge’]で全件ループする手法もあるかと思いますが、APIによる画面セットは、常に全項目ではなく、一部のみのケースも多々あるため、JSONでのループとしています。

setInputValue()にはない機能について

例として挙げたsetInputValue()にはない機能については以下のとおり。

  • 複数選択可能なSELECTには対応していません
    (理由はあまり見かけないからですが、もし必要ならば、ロジックを改良してみてください)
  • INPUTタグ優先ですが、idを優先したい場合は、ロジックを入れ替えてください
  • idは(セキュリティ面を考え)innerTextとしています

innerTextをinnerHTMLに変更すると、JSON内にHTMLタグが混入していた場合、有効化してしまいます。
その可能性がある項目は、何らかの対策を施す必要がありますので、それが面倒であるのならば、innerTextのままにしておきましょう。
(innerTextのままですと、タグがそのまま表示されます。)

TEMPLATE内には値をセットできません

前回同様、TEMPLATEタグには制限があります。

TEMPLATE内にはアクセスできない

前回もそうだったように、TEMPALTEタグは、ドキュメント対象外となるので、値をセットすることは出来ません。

解決策は

クローンをコピペする段階でnameやidが認識できるようになります。
ただしnameもidもユニークであることが求められます。

TEMPLATEについては、主に明細行追加や一覧画面に使用します。
気になる方はこちらの記事も参照してみてください。

最後に

だいぶ進みましたね。

残された機能は、値によって画面に動きをつけたり、エラーチェックをAPIで行ったり、明細行に対応したり…でしょうか。
また、このチュートリアルの範囲からは外れるかもしれませんが、共通箇所のローディング手法なども、まだ、取り上げてはいませんね。

次回は、その中の、値によって画面に動きをつけたりします。
さらに初期値をセット時にも活用できるようにします。

それでは、また。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA

目次