【第10回】同じアプリを、自分とAIの両方で作ってみる|総合演習(嫁子が教えるTS講座)
今回のゴール
- 第2部で学んだことを使って、実際に動くものを1つ完成させる
- 「データを持ち、それを画面に描く」という基本の形を体験する
- 同じものをAIに作らせるときの指示文を書けるようになる
第2部(JavaScriptの基礎)の最後です。変数からDOM操作まで、ここまでの6回が1本につながります。全体の流れは講座の目次へ。
嫁子: 今日は2部構成でいくね。前半は自分で組み立てて、後半は同じものをAIに作らせる練習をするよ。
変な順番に見えるかもしれないけど、逆なの。一度自分で作ったものだからこそ、AIの出してきたコードの良し悪しが判断できるんだよ。
この講座のゴールは「全部自分で書ける人」じゃなくて「判断できる人」だったでしょ。今日はその両方をやる回だね。
作るもの
品物を追加して、買ったらチェックを入れ、消せるだけのメモです。
- 入力欄に品名を入れて「追加」で一覧に増える
- 品名をクリックすると打ち消し線がつく(買った印)
- 「削除」で消える
- 残りの件数が表示される
見た目は素っ気なくていいので、動く仕組みを作ることに集中します。
① HTML
index.html として保存します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>買い物メモ</title>
<style>
body { font-family: sans-serif; max-width: 480px; margin: 2rem auto; }
li { margin: .4rem 0; }
.done { text-decoration: line-through; color: #999; }
</style>
</head>
<body>
<h1>買い物メモ</h1>
<input id="input" type="text" placeholder="牛乳">
<button id="add">追加</button>
<p id="count"></p>
<ul id="list"></ul>
<script src="app.js"></script>
</body>
</html>
.done というCSSのクラスを用意しておきます。買った印は、このクラスを付け外しして表現します。
② データを決める
コードを書く前に、何を持つかを決めます。第5回でやった「オブジェクトの配列」です。
let items = [];
// 中身はこうなる予定
// [{ name: "牛乳", done: false }, { name: "卵", done: true }]
品名(name)と、買ったかどうか(done)の2つだけ持ちます。
const ではなく let にしているのは、配列そのものを入れ替えるからです(削除のときに使います)。
嫁子: ここ、実は今日いちばん大事なところなの。
初心者がやりがちなのは、画面に書いた文字を「データ」として扱うこと。表示されている文字を読み取って処理する、みたいなやり方ね。動くんだけど、すぐに破綻するよ。
データが正で、画面はその写し。この考え方は第4部のReactでもそのまま使うから、今のうちに体に入れておいてね。
③ 画面を描く関数
データを見て、画面を作り直す関数を1つ用意します。ここが今回の中心です。
const list = document.getElementById("list");
const count = document.getElementById("count");
function render() {
list.textContent = ""; // いったん空にする
items.forEach((item, index) => {
const li = document.createElement("li");
const span = document.createElement("span");
span.textContent = item.name;
if (item.done) {
span.classList.add("done");
}
span.addEventListener("click", () => {
items[index].done = !items[index].done;
render();
});
const del = document.createElement("button");
del.textContent = "削除";
del.addEventListener("click", () => {
items.splice(index, 1);
render();
});
li.appendChild(span);
li.appendChild(del);
list.appendChild(li);
});
const remain = items.filter((item) => item.done === false).length;
count.textContent = `残り ${remain} 件`;
}
初めて出てくるものだけ補足します。
| 書き方 | 意味 |
|---|---|
createElement("li") |
HTMLの要素を新しく作る |
appendChild(...) |
作った要素を中に入れる |
splice(index, 1) |
配列から1件を取り除く |
!item.done |
true と false を反転させる |
毎回まっさらにしてから全部描き直しています。「1件追加されたから、その1件だけ足す」という書き方もできますが、そちらは書き換え漏れが起きやすくなります。
嫁子: 全部描き直すって、無駄が多いように見えるでしょ。実際ちょっと無駄なの。
でも「データと画面がズレない」ほうが、初心者には圧倒的に大事なんだよ。速さの心配をするのは、遅くなってからでいい。
ちなみにReactは、この「全部描き直す」を宣言しつつ、裏で変わった部分だけを差し替えてくれるの。だから無駄がない。今日の面倒くささが、そのままReactの存在理由だね。
④ 追加の処理
const input = document.getElementById("input");
document.getElementById("add").addEventListener("click", () => {
const name = input.value.trim();
if (name === "") {
return; // 空なら何もしない
}
items.push({ name: name, done: false });
input.value = "";
render();
});
render(); // 最初に一度描く
trim() は前後の空白を取り除きます。空白だけの入力を弾くために必要です。第6回で触れた「空文字は false 扱い」の話が、ここで実用になります。
最後の render() は、ページを開いたときに一度描くための呼び出しです。第7回で言った「関数は呼ばないと動かない」がここです。
これで完成です。ブラウザで index.html を開くと動きます。
⑤ 後半:同じものをAIに作らせてみる
ここからが本番です。同じアプリを、今度はAIに作らせます。
悪い指示の例。
買い物リストを作って
これだと、データの形も、削除の有無も、全部AIが勝手に決めます。返ってきたものが気に入らなくても、どこを直せばいいか言えません。
良い指示の例。
HTMLとJavaScriptだけで動く買い物メモを作ってください。 - データは
{ name: string, done: boolean }の配列で持つ - 入力欄と追加ボタン、一覧、残り件数を表示 - 品名をクリックするとdoneが反転し、打ち消し線がつく - 各行に削除ボタン - 空文字と空白だけの入力は追加しない -innerHTMLは使わずtextContentとcreateElementで組む
違いは、データの形と条件を先に決めていることです。これは今日の②で自分がやった作業そのものです。
| 指示に入れるもの | 今日どこで学んだか |
|---|---|
| データの形 | 第5回・今日の② |
| 何を関数に分けるか | 第7回 |
innerHTML を避ける |
第9回 |
| 空入力の扱い | 第6回 |
嫁子: 「AIに何を指示すればいいか分からない」の正体は、たいてい「自分が何を作りたいか決まっていない」なの。技術の問題じゃないんだよね。
データの形を決めるのはあなたの仕事。それを実装するのはAIの仕事。この分担ができた人が、いちばん速いと思う。
出てきたコードを読んで「
doneの反転が抜けてる」と言えたら、今日の目標は達成だよ。
つまずきポイント
動かないときは、まずF12でConsoleを見てください。赤い文字でエラーが出ているはずです。第9回でも触れましたが、エラー文はそのままAIに貼れば伝わります。
何も起きないときは render() の呼び忘れを疑ってください。データは増えているのに画面が古いまま、という状態です。console.log(items) を挟むと、データ側は動いているかどうかが分かります。
今回のまとめ
- データ(オブジェクトの配列)を持ち、それを見て画面を描く、が基本の形
- データが正で、画面はその写し。画面から情報を読み取る作りにしない
render()を1つ用意して、変更のたびに呼ぶと、ズレが起きない- AIへの指示は、データの形と条件を先に決めると精度が上がる
- 手で全部書き換える方式の面倒さが、Reactが存在する理由
次にやること
第2部(JavaScriptの基礎)はこれで終わりです。次回から第3部・TypeScriptに入ります。
まずはなぜ型が要るのかからです。今日作ったアプリには、まだ静かに壊れる余地が残っています。done に文字列を入れても動いてしまいますし、name を打ち間違えても undefined が返るだけです。それを書いた瞬間に止めるのが、次の部の主題です。
今回の宿題:完成したアプリを、AIにも同じ条件で作らせて、自分のコードと見比べてください。違うところがあれば、どちらが良いかではなく「なぜそう書いたか」を聞いてみると、いちばん勉強になります。
嫁子: AIに「なんでこう書いたの?」って聞くの、遠慮しなくていいよ。ちゃんと答えてくれるから。
人に聞くのは気が引けるけど、AIには何度でも聞けるでしょ。これ、独学の最大の弱点だった「聞ける人がいない」が消えたってことなの。使わない手はないよ。