プログラマー VTuber 衣亥栖ティオのちょっとした話

Youtubeに投稿したプログラミング学習動画の補足説明をするためのブログです。

プログラマーへの道 #33 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

CSSのdisplay属性

動画内ではエラーメッセージのHTMLを表示したり、非表示にするのにCSSのdisplay属性を利用しています。

display - CSS: カスケーディングスタイルシート | MDN

値として"none"を指定するとHTMLが非表示になり、HTMLが存在していた場所は他のHTML要素によって詰められます。 値として空文字を指定するとHTMLが表示されます。

displayと似た属性としてvisibilityという属性も存在します。

visibility - CSS: カスケーディングスタイルシート | MDN

visibilityを利用してもHTMLの表示/非表示を切り替えることができますが、 HTMLが存在していた場所が他のHTML要素によって詰められることはないので、 HTMLが存在してい場所が空白として残ります。 今回のエラーメッセージでは空白を残したくなかったので、displayを利用しています。

Twitter Bootstrapのアラートについて

今回はエラーメッセージのデザインとしてアラートを利用しました。

Alerts · Bootstrap v5.1

アラートにはいくつか種類があります。 今回はエラーであることが分かる赤いアラートにしました。

Alerts · Bootstrap v5.1

アイコン付きのアラートもあるようなので、こちらの方が良かったかもしれません。

Alerts · Bootstrap v5.1

プログラマーへの道 #32 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

バリデーションという単語の意味

動画内ではタイトルの文字数をチェックするif文を実装しました。 このように値の正しさをチェックすることを "バリデーション" といいます。 バリデーションは英語の名詞である validation のことで、日本語では "検証" という意味があります。 動詞だと valid です。 エンジニア同士の会話では「タイトルのバリデーションの実装はまだ終わっていません」のように使います。 よく使う言葉なので覚えておきましょう。

タイトルの文字数のバリデーション

動画内では if を利用してタイトルの文字数のバリデーションを実装しました。

if (title.length < 1 || title.length > 30) {
}

文字列の文字数は length プロパティで取得することができます。

String length - JavaScript | MDN

ちなみに配列の長さも length で取得することができます。

Array.length - JavaScript | MDN

if の条件は "文字列の長さが1文字未満、もしくは30文字以上の場合" という条件になっています。 "もしくは" を表現しているのは || です。

プログラマーへの道 #31 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

メモの登録は専用のモーダルを用意する

前回のブログでメモを登録するためのモーダルを編集用のモーダルと一緒にするかどうかを説明しました。 最終的にはメモを登録するための専用のモーダルを用意しています。 実際にこの方針で実装してみると、プログラムがとてもシンプルになったと思っています。 同じようなモーダルのHTMLが2つあるので、モーダル部分を修正する場合、2つのHTMLを修正しなければいけませんが、 今の所そういった予定はないので、一旦このまま進めようと思います。

モーダルのHTMLの id である "inputGroup-sizing-default" について

動画中で "inputGroup-sizing-default" という id が編集用のモーダル内のHTMLと登録用のモーダルのHTMLに存在します。

<span class="input-group-text" id="inputGroup-sizing-default">タイトル</span>

id の値は本来HTML上でユニークでなければいけません(重複してはいけません)。 しかし、動画中では重複しても問題ないと判断し、特に修正しませんでした。

以下のように "inputGroup-sizing-default" という id は aria-describedby という属性で指定されています。

<div class="input-group mb-3">
  <span class="input-group-text" id="inputGroup-sizing-default">タイトル</span>
  <input type="text" id="id-add-modal-title" class="form-control" aria-label="Sizing example input"
  aria-describedby="inputGroup-sizing-default">
</div>

aria-describedby の説明は以下です。 値が重複したとしても今回のメモ帳アプリが動かなくなることはないと思い、重複を許容しています。

aria-describedby 属性の使用 - アクセシビリティ | MDN

開発方針について

動画の最後の方でも言及したのですが、 私がアプリケーションを開発するときに "まずは動くものを雑に作って、それが完成したら細かいところを作り込む" という方針を取ります。 動くものを最初に作ることによって、 プログラムの全体像(どこが複雑になりそうか? このまま作り続けて問題ないプログラムになっているか?)を早い段階で確認できたり、 メモ帳アプリ自体の使い勝手も確認することができます。

これはプロダクト開発における "MVP(Minimum Viable Product)" という考えをプログラミングにも応用したものです。

【起業家必見】MVP開発とは何か? – techpartner

プログラムを書く→修正する→書く→修正する→書く→修正する ... というサイクルを早く回すことによって、 プログラムの品質を段階的に向上させていくことができます。 動くものが早くできるので、利用者に使ってもらって、良し悪しを確認してもらうこともできます。

プログラマーへの道 #30 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

メモの登録をモーダルにする実装方針について

今回の動画はメモの登録をモーダルに変更するのが目的でした。 動画中では結局決められなかったのですが、メモの登録をモーダルに変更する方法としては以下の2つがあります。

  1. メモを編集する際に利用するモーダルとは別で登録用のモーダルを用意する。
  2. メモを編集する際に利用するモーダルを登録でも利用する。

それぞれ説明します。

1. メモを編集する際に利用するモーダルとは別で登録用のモーダルを用意する。

1番は登録用のモーダルを新規実装することになるので、考え方としてはシンプルなものになります。 具体的にはモーダル用のHTMLをそれぞれ用意するというものです。 しかし、編集用のモーダルと登録用のモーダルが2つ存在することになるので、モーダルのデザインを変更する際に2つのHTMLを修正する必要があります。

プログラミングでは共通化できるものは共通化するという "DRY(Don't Repeat Yourself)" という考え方があるので、 同じようなHTMLを2つ用意するのは避けた方がよいという考え方ができます。

Don't repeat yourself - Wikipedia

実際に動画中でもHTMLを2つ用意しようと思いましたが、上記の理由に加えてHTMLの id の値の重複を解消するのが面倒で手を止めました。 id の値に統一感がなくなり、管理できなさそうだと感じたからです。

2. メモを編集する際に利用するモーダルを登録でも利用する。

2番はメモを編集する際に利用するモーダルを登録でも利用するというものです。 これは1番で問題に感じていた以下の2点を解消することができます。

  • HTMLを2つ用意する。
  • id の重複を解消する。

一見良さそうです。 実際に私も最初に選択したのは2番の方針でした。

しかし、実際に実装しようとすると "登録と編集の細かい違い" によって実装難易度が予想以上に高いことに気づきました。 例えば以下の違いがあります。

  • モーダルの上部に表示されるテキストが異なる。登録時は "登録" になるが、編集時は "編集" になるので、モーダルを表示する際に文字を切り替えなければいけない。
  • モーダルの下部に表示されるボタン(現在は更新ボタン)のテキストが異なる。登録時は "登録" になるが、編集時は "編集" になるので、モーダルを表示する際に文字を切り替えなければいけない。
  • モーダルの更新ボタンを押したときの挙動が異なる。登録時はメモの一覧にメモを追加するが、編集時はメモ一覧の値を書き換える。
  • モーダルを表示する際の挙動が異なる。登録時はタイトルと本文が空でよいが、編集時は編集対象のタイトルと本文をセットする必要がある。

この細かい違いに対応するためにif文が増えてしまうと、プログラム自体が複雑になってしまいます。 動画の視聴者だけでなく、実装者である私が理解するのが難しいプログラムになってしまうようでは意味がありません。


動画中で1番と2番の実装方針を決めきることができないと感じたので、 今回は動画を切りました(時間も10分を超えていたのでちょうどよかったというもあります)。

このブログを書いている時点では、一旦1番でシンプルに実装してみて、問題があるようであれば、2番で実装しようかなと思っています。

プログラマーへの道 #29 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

コードのコピペについて

現在実装中のメモ帳アプリでは同じようなコードをコピペして複数箇所で利用しています。 例えば以下のような現在時刻を取得する処理です。 現在このコードは2箇所に存在します。

const now = new Date()
const updatedAt = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate()

同じようなコードが複数箇所に存在すると、そのコードを修正する際に複数箇所を修正しなければいけません。 例えば日付のフォーマットを変更する場合、2箇所のコードを修正する必要があります。 そのため、同じようなコードは関数でまとめるのがセオリーです。

function getCurrentDate() {
    const now = new Date()
    return now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate()
}

これによって日付のフォーマットを変更する際に getCurrentDate() という関数を修正するだけで済みます。

動画では "とりあえず動くものを作る" という方針なので、 今まで通りコピペで実装を進めていきますが、 メモ帳アプリが完成したタイミングで↑のようなプログラムの修正を加えていく予定です。

モーダルを閉じる方法

hide() を利用することでモーダルを閉じることができます。

Modal · Bootstrap v5.1

モーダルを操作するオブジェクトの定義方法について

動画でモーダルを閉じる際に上手く行かなかった点について補足します。 動画では以下のようなコードを書きましたが、うまくいきませんでした。

const updateButton = document.getElementById("id-update-button")
updateButton.addEventListener("click", (event) => {
    
    //省略

    //これは上手く動かなかった。
    const myModal = new bootstrap.Modal(document.getElementById('exampleModal'))
    myModal.hide()
})

しかし、myModal という変数をグローバル変数にするとうまくいきました。

<script>
  const titleElement = document.getElementById("id-title")
  const bodyElement = document.getElementById("id-body")

  //以下のように定義した myModal はどこからでも参照できるので、グローバル変数となる。
  const myModal = new bootstrap.Modal(document.getElementById('exampleModal'))

なぜ最初の方法がうまく動かなかったのかというと、 new bootstrap.Modal() によって生成されるモーダルのオブジェクトはモーダルの状態を管理しているからです。 モーダルには閉じている状態と開いている状態があります。 そのため、同じモーダルのオブジェクトに対して show(), hide() を実行しないと上手く動かなくなってしまいます。

以下の更新ボタンで作ったモーダルオブジェクトと

const updateButton = document.getElementById("id-update-button")
updateButton.addEventListener("click", (event) => {
    
    //省略

    const myModal = new bootstrap.Modal(document.getElementById('exampleModal'))
    myModal.hide()
})

以下の編集ボタンで作ったモーダルオブジェクトは異なるオブジェクトとなります。

const editButton = document.getElementById(editButtonId)
editButton.addEventListener("click", (event) => {
    //省略

    const myModal = new bootstrap.Modal(document.getElementById('exampleModal'))
    myModal.show()
})

そのため、更新ボタンを押した際に myModal.hide() を実行していますが、 それは開いたモーダルとは別のオブジェクトに対して閉じる操作をしたことになるので、 開いたモーダルは反応しませんでした。

プログラマーへの道 #28 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

inputタグの type="hidden" について

inputタグの type="hidden" についての説明は以下です。 主に "画面に表示する必要はないけど、値は使いたい場合" に利用する type となっています。

developer.mozilla.org

動画中ではJavaScriptにてinputタグを作成して、trタグに追加しています。

const inputForBody = document.createElement("input")
inputForBody.setAttribute("id", "id-body-in-list-" + memoId)
inputForBody.setAttribute("type", "hidden")
inputForBody.value = body
tr.appendChild(inputForBody)

labelタグとfor属性について

動画内のモーダルのHTMLでは label タグを利用しています。 labelタグの公式説明は以下になりますが、英語なのでGoogle翻訳などで翻訳してみてください。

HTML <label> Tag

日本語だと以下が分かりやすいと思います。

【HTML】lavelタグにfor属性を付けるべき理由とは?使い方まで徹底解説! | ポテパンスタイル

プログラマーへの道 #27 の補足説明

こんにちは、プログラマーVTuberの衣亥栖ティオです。
今回は以下の動画の補足説明をします。 動画内で実装したソースコードも載せています。


今回の動画で実装したソースコード

以下が今回実装したソースコードです。

押された編集ボタンの要素を取得する

動画ではメモ一覧に表示されているタイトルをモーダルにセットする処理を作っていますが、 その際に "押された編集ボタンの要素" を取得しています。 実際のコードが以下です。

editButton.addEventListener("click", (event) => {
      const memoId = event.currentTarget.dataset.memoId

     //省略
})

addEventListener() の引数である event にはクリックに関する様々な情報が格納されています。

Event - Web API | MDN

上記のコードでは押された編集ボタンの要素である currentTarget を利用することで、 編集ボタンにセットした memo-id を取得しています。

Event.currentTarget - Web API | MDN

getElementById() から直接プロパティを参照する

以下のように getElementById() に直接 ".(ドット)" をつなげることで、直接プロパティを参照することができます。

const editButton = document.getElementById(editButtonId)
  editButton.addEventListener("click", (event) => {
  const memoId = event.currentTarget.dataset.memoId

  // innerText を直接参照することで、title 変数にはタイトルの文字列が代入される。
  const title = document.getElementById("id-title-in-list-" + memoId).innerText

  //省略

以下のように titleInListElement という変数にHTML要素を格納してから、 title 変数にタイトルを代入してもよいのですが、 HTML要素を titleInListElement 変数を利用する必要がない(無駄な変数を定義する必要はない)ので、 getElementById() から直接プロパティを参照する方法で実装しました。

const editButton = document.getElementById(editButtonId)
  editButton.addEventListener("click", (event) => {
  const memoId = event.currentTarget.dataset.memoId

  // HTML要素を取得するだけにする。
  const titleInListElement = document.getElementById("id-title-in-list-" + memoId)

  // タイトルを title 変数にセットする。
  const title = titleInListElement.innerText

  //省略

innerText と value の違い

Inputタグの値は value で取得しますが、HTMLタグの中にセットされている値は innerText で取得する必要があります。