プログラミングの理解が遅すぎる初心者がJavaScript、Node.jsで投票型掲示板を作ろうとしてます

トップページでは記事の順番がごちゃごちゃなので、記事もくじをご覧いただければと思います。

掲示板のサーバーを作る⑧フォームに書き込んだ内容の動きを見る

前回までで掲示板の骨子はできあがりました。

もくじの「バックエンドを使った掲示板」を見ていただければ、その変遷はわかっていただけると思います。

私もわからなくなるたび、これらの記事を見直すことになります。

で。

フロント側、サーバー側のコードを書いて、コード内での繋がりは理解しました。

どこで定義した関数が、どこで実行されてるとか、そういう感じのやつです。

ですがサーバー側から送り出したデータがフロントのどこで受け取って、どう処理されているか、というところが少し分かりづらいところがあります。

なので、今回はフォームに書き込んだ名前とコメントが、どのような道筋をたどっていってページに反映されるのかということを見ていきたいと思います。

では、まずはコードを書いていきます。

HTML
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>掲示板</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>掲示板</h1>
    <form id="form" action="#"> 
    <!-- 今はactionの内容は必要ないのでこのようにしています -->
        <label for="name">名前</label>
        <input type="text" name="name" id="name" placeholder="名前を入力してください">
        <label for="comment">コメント</label>
        <textarea name="comment" id="comment" placeholder="コメントを入力してください"></textarea>
        <button id="submit">送信</button>
    </form>
    <div id="posted"></div>
    <script src="script.js"></script>
</body>
</html>
フロントエンドのコード
console.log('script.js は読み込まれました');

document.getElementById('form').addEventListener('submit', async (e) => {
  e.preventDefault();
  console.log('submitイベント発火');
  const name = document.getElementById('name').value.trim();
  const comment = document.getElementById('comment').value.trim();

  if (!name || !comment) {
    alert('名前とコメントを入力してください');
    return;
  }

  try {
    const res = await fetch('http://localhost:3000/api/messages', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, comment }),
    });

    if (!res.ok) throw new Error('送信に失敗しました');

    const data = await res.json();
    showPostedMessage(data);
    document.getElementById("comment").value = "";//コメント内容をクリア
    fetchMessages(); // 任意:投稿一覧を取得して再表示
  } catch (error) {
    alert(error.message);
  }
});

function showPostedMessage(message) {
  const postedDiv = document.getElementById('posted');
  const newMessage = document.createElement('div');
  newMessage.innerHTML = `
    <p><strong>${escapeHTML(message.name)}</strong> さんのコメント:</p>
    <p>${escapeHTML(message.comment)}</p>
    <hr>
  `;
  postedDiv.prepend(newMessage);
}

async function fetchMessages() {
  try {
    const res = await fetch('http://localhost:3000/api/messages');
    if (!res.ok) throw new Error('投稿一覧の取得に失敗しました');
    const messages = await res.json();
    const postedDiv = document.getElementById('posted');
    postedDiv.innerHTML = '';
    messages.forEach(showPostedMessage);
  } catch (error) {
    console.error(error);
  }
}

function escapeHTML(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

fetchMessages(); // ページ読み込み時に一覧を取得
サーバー側のコード
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = 3000;

app.use(cors());
app.use(express.json());

let messages = [];

app.get('/api/messages', (req, res) => {
  res.json(messages);
});

app.post('/api/messages', (req, res) => {
  console.log('POST受信:', req.body);
  const { name, comment } = req.body;
  if (!name || !comment) {
    return res.status(400).json({ error: '名前とコメントは必須です' });
  }
  const newMessage = { name, comment };
  messages.push(newMessage);
  res.status(201).json(newMessage);
});

app.listen(PORT, () => {
  console.log(`サーバーがポート${PORT}で稼働中`);
});

んではやっていきましょう

フォームから送信する

名前は「オカルン」。

コメントは「モモちゃんはよお、そんなんじゃねえんだわ」。

これでいきましょう。

これで送信ボタンを押します。

送信ボタンを押す

送信を押すからまず

document.getElementById('form').addEventListener('submit', async (e) => {
  e.preventDefault();

これが動作します。 e.preventDefault();が動くのでページの更新をしないようになります。

const name = document.getElementById('name').value.trim();
  const comment = document.getElementById('comment').value.trim();

  if (!name || !comment) {
    alert('名前とコメントを入力してください');
    return;
  }

これで名前とコメントにスペースなどの見えない間隔があった場合はそれらが削り取られ、どちらかが空欄であった場合はアラートとともに処理がストップします。

fetchでデータをサーバーに送信する
const res = await fetch('http://localhost:3000/api/messages', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, comment }),
    });

行先、方法、ヘッダー、ボディを含めたデータを送信する命令を出します。

名前、コメントはボディの中に入っています。

データを受け取る

データを受け取るルートは二つあります。

一つは書き込んだ内容をサーバーに保存してから反映するルート、もう一つはサーバーに保存しないルート。

サーバーが混雑していた場合、サーバーに保存するルートのみでは画面が止まってしまうことがあります。

なので「とりあえず送信はできました」ということを確認できるようにするため、サーバーにデータを送りはするけど保存をせず、まずは反映を優先するルートを作ります。

このルートではフロントからサーバーにデータを送信はするけど取得する必要はないので、GETリクエストを送る必要はありませんし、サーバー側もGETリクエストが来た時の処理をする必要はありません。

なので単純に以下のようにします。

const data = await res.json();
function showPostedMessage(message) {};

こう処理すれば普通にHTMLに反映されることになります。

次にサーバーに保存するルートです。

まずは

let messages = [];

で配列を作っておきます。

そしてサーバーに保存された配列に、送られてきた名前とコメントのデータを付け足します。

サーバーにデータを保存する
app.post('/api/messages', (req, res) => {
  console.log('POST受信:', req.body);
  const { name, comment } = req.body;
  if (!name || !comment) {
    return res.status(400).json({ error: '名前とコメントは必須です' });
  }
  const newMessage = { name, comment };
  messages.push(newMessage); 
  res.status(201).json(newMessage);
});

送られてきたボディから名前やコメントを取り出しオブジェクト化したあと、そのオブジェクトをmessages.push(newMessage);で配列messagesに付け加えています。

ただしその前に、if (!name || !comment) {}で保存データに異常がないかをチェックしています。

これでサーバーは新しい情報を保存することになりました。

次はこの新しい情報をHTMLに反映させます。

書き込みをHTMLに反映させる

フロント側からきた「データをよこせ」というリクエストに対応するコードが以下となります。

app.get('/api/messages', (req, res) => {
  res.json(messages);
});

messagesJSON化する処理をしています。

サーバーから送られてきたデータは

async function fetchMessages() {};

で処理されます。

ここでは

const res = await fetch('http://localhost:3000/api/messages');

によってサーバーのデータを取得します。

(取得の方法を記載していない場合はデフォルトとしてGETが適用されます。)

で、これを

const messages = await res.json();
    const postedDiv = document.getElementById('posted');
    postedDiv.innerHTML = '';
    messages.forEach(showPostedMessage);

こんな感じでいじくっているわけですが、要は送られてきたデータをJSON形式にしてmessagesに代入したあと、用意しておいたdivタグの中身をいったん空白にして、先ほどのmessagesのデータを片っ端から参照し、showPostedMessage()、つまり書き込んでいくということです。

ちなみにshowPostedMessage()ではpostedDiv.prepend(newMessage);と書いてありますので、新しい投稿は上に付け加えられていくことになります。

おわりに

フォームに書き込んだデータの大まかな動きを追ってみました。

結果はこんな感じです。

Image
オカルン

失敗したときの処理、などはその場で見ればわかりますので省きましたが、おおよその流れはわかったように思います。

次回は…どうしよう。

CSSでページをきれいに整えるか、サーバーを扱えるようにMySQLを勉強していくかのどちらかになります。

が、まずは今までほとんど手を付けてこなかったCSSをやろうかなと思います。

Image