つくる ポートフォリオアプリを仕上げる
このレッスンで分かること
- 覚えておく値は 1 か所に置き、描き直しは 1 本の関数にまとめます
- しぼり込みと並び替えを、元の配列を壊さずに掛け合わせます
同じことを 2 か所が覚えると、必ずどちらかが古くなる
このコースの総仕上げです。API から作品を取り、公開ずみだけを選び、タグでしぼり、並び替え、問い合わせフォームの入力を確かめるところまでを 1 枚に集めます。新しい構文は出てきません。ばらばらに練習したものを同居させても壊れない形に置くことが主題です。
まず、このアプリが覚えておく必要のあるものは 3 つだけです。届いた作品の全部と、いま選ばれているタグと、いま選ばれている並び順。ただし後ろの 2 つは select 要素そのものが覚えているので、別の変数に写す必要はありません。
JavaScript
// 危ない形。選択が変わるたびに 2 か所を直さないと食い違う
let currentLevel = "all";
levelSelect.addEventListener("change", function () {
currentLevel = levelSelect.value;
});写した瞬間に、正しい値がどちらなのか分からなくなります。select の値がほしいときは、そのつど .value を読めば済みます。
今回の決まりは次のとおりです。
- 公開ずみの作品だけを、選ばれたタグでしぼる
- 表示回数の多い順、少ない順、タイトル順の 3 通りで並べ替える
- しぼり込みか並び順が変わったら、一覧と件数を出し直す
- 通信に失敗したら、一覧を空にして理由を出す
- 送信されたら、名前が空でないことと、メールアドレスの形を確かめる
どちらが変わっても、描き直しは 1 本
しぼり込みと並び替えを別々のイベントで別々に画面へ反映すると、タグを変えたら並び順が既定に戻った、という組み合わせの不具合が出ます。どちらが変わっても同じ 1 本を呼ぶ形にすると、この種の不具合はそもそも起きません。
JavaScript
levelSelect.addEventListener("change", paintSkills);
orderSelect.addEventListener("change", paintSkills);その 1 本が呼ぶ「いま出すぶんを決める関数」は、画面を触りません。覚えている配列と 2 つの select を読んで、配列を返すだけにします。しぼるのは filter、並べるのは sort ですが、sort は元の配列を書き換えるので、写しを作ってから掛けます。
JavaScript
const picked = allSkills.filter(function (skill) {
return skill.visible;
});
const sorted = [...picked];
sorted.sort(function (a, b) { return b.months - a.months; });filter が新しい配列を返しているので上の例は無事ですが、覚えている配列に直接 sort を掛けると、もとの順番は二度と戻りません。写しを作る癖を付けておくほうが安全です。日本語の名前順にしたいときは a.name.localeCompare(b.name, "ja") を使います。
失敗した画面と、入力を止める画面
fetch は 404 でも 500 でも catch に入りません。response.ok を自分で見て、false なら投げます。そして catch の中では、一覧を空にし、件数を 0 に戻してから理由を出します。前回の表示が残っていると、利用者には古いデータが最新に見えるからです。
入力の検証は送信の直前に 1 回だけ行います。見つけたエラーは配列にためて、最後に 1 回で出してください。
JavaScript
const errors = [];
if (name === "") {
errors.push("お名前を入力してください");
}1 つ見つけた時点で return すると、直すたびに次のエラーが出てくる形になります。名前も空でメールも変、という場合に両方を伝えられるほうが親切です。
よくある間違い
- 描き直しの前に空にし忘れる — しぼり込みを切り替えるたびにカードが下へ積み上がります。件数の表示だけが正しく、画面と食い違います
awaitを書かずにresponseを使う —responseは箱なのでokはundefinedです。undefinedは falsy なので、通信が成功していても必ずエラー側に入ります。いつも読み込めませんでしたと出るときは、まずここを疑ってください
課題
- createWorkCard で article.work-card を作る。中に h3 でタイトル、p.work-meta で「2026 年 / 120 回表示」の形、p.work-tags でタグを入れる
- visibleWorks で allWorks から published が true のものだけを選び、#tag-filter の値でしぼり、#sort-order の値で並べ替えた配列を返す。all のときはしぼらない。sort の前に配列の写しを作る
- 並び順は views-desc が表示回数の多い順、views-asc が少ない順、title がタイトル順。既定は views-desc
- render で #work-list を空にしてから描き直し、#works-count に「4 件を表示中」の形で件数を出す。#tag-filter と #sort-order の change でこれを呼ぶ
- loadWorks で https://yumesaku.tech/course-api/works.json を await fetch する。response.ok が false なら throw し、成功したら #works-status に「5 件を読み込みました」の形で出して render する
- 失敗したときは catch で #work-list を空にし、#works-count を「0 件を表示中」にして、#works-status に読み込めなかったことと理由を出す
- 送信では preventDefault してから #name が空でないことと、#email が正規表現に合うことを確かめる。だめなら #form-error に理由を出して contact-form に has-error を付け、よければ #form-error を空にし has-error を外して #form-message に「送信を受け付けました」を含む文を出す