fetch テーブル表示とバリデーション関数化　

### 関数化　考え方

```php:
 関数化する理由  : 後から修正したい時、修正しやすくなる
 　　　　　　      codeが短くなる

 やり方 : 何回も同じ処理使ってるもんを関数にする

 デメリット : 初心者には、わかりにくい
```

### 関数化　主な仕組み

```php:
 何回も使ってる処理を、見つける
 処理を見つけたら、関数定義
 ボタン押されたら、ボタンが押された後に先に関数呼び出す
 ボタンが押された後、先に呼び出しといた関数を定義
 
 (data)と(formData)をつける基準 : 
   → その時、そこに使いたい渡さなきゃいけないdataが
   　 その時ある : かく
   → その時、そこに渡さなきゃいけないdataがない : 何もなし     ```


### テーブル表示時、バリデーションしたいを関数にしたら

```php:考え方
  data送る前に、バリデーション
  data送って、data送信に成功したらテーブル表示
```

<br>
### 送信ボタンを押したら　考え方

```php:
document.addEventListener('DOMContentLoaded', () => {
    ID呼び出し(data送る前準備)

    送信ボタン押したら
    [関数 : data表示 * 空白消す]を、変数"formData"に入れる
    → dataの読み込みは何回も使うから、変数に入れてる
    → data読み込み関数を変数に入れずに何回も
    　呼び出すと、入れたdataが違う結果に。
     
    関数 : data表示 * 空白消す
    関数 : バリデーション初期化
    関数 : バリデーション
    関数 : data送信
    data送信に成功したら
    関数 : テーブルにdata表示
    関数 : 入力form空欄にする
    data送信失敗したら
    エラー出す

    関数 : data表示 * 空白消す 中身定義

    関数 : バリデーション  中身定義

    関数 : バリデーション初期化 中身定義

    関数 : data送信　 中身定義
    
    関数 : テーブルにdata表示 中身定義

    関数 : 入力form空欄に　中身定義


    //ボタン押された時のもののみ、code化すると
    button.addEventListener('click', () => {
    const formData = getFormData();
    clearErrors();
    if (!validateForm(formData)) return;

    sendData(formData)
      .then(() => {
        addTableRow(formData);
        clearFormInputs();
      })
      .catch(() => {
        alert('送信に失敗しました');
      });
  });
```

###フラッシュメッセージ　導入考え方

```php:
同じページでメッセージ表示しすぐ消す : style.displa(JS)
違うページでメッセージ表示しすぐ消したい : Flask フラッシュメッセージ
```

### code全文

```php:
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>テーブル表示練習</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <style>
    .form-group { margin-bottom: 10px; }
    .label { display: inline-block; width: 80px; }
    .error-message { display:none; color:red; margin-left:5px; }
    table, th, td { border:1px solid #000; border-collapse:collapse; padding:5px; }
  </style>
</head>
<body>

<form id="userForm">
  <div class="form-group">
    <label class="label" for="studentName">名前</label>
    <input type="text" id="studentName">
    <div id="nameError" class="error-message" style="display:none;color:red;"></div>
  </div>
  <div class="form-group">
    <label class="label" for="studentAge">年齢</label>
    <input type="number" id="studentAge">
  </div>
  <div class="form-group">
    <label class="label" for="studentEmail">メール</label>
    <input type="email" id="studentEmail">
  </div>
  <div class="form-group">
    <label class="label" for="studentnumber">電話</label>
    <input type="text" id="studentnumber">
  </div>
  <button type="button" id="addButon">追加</button>
</form>

<hr>

<table id="studentTable">
  <thead>
    <tr><th>名前</th><th>年齢</th><th>メール</th><th>電話</th></tr>
  </thead>
  <tbody></tbody>
</table>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('userForm');
  const button = document.getElementById('addButon');

  const nameInput = document.getElementById('studentName');
  const ageInput = document.getElementById('studentAge');
  const emailInput = document.getElementById('studentEmail');
  const phoneInput = document.getElementById('studentnumber');

  const nameError = document.getElementById('nameError');
  const tableBody = document.querySelector('#studentTable tbody');

  button.addEventListener('click', () => {
    const formData = getFormData();
    clearErrors();

    if (!validateForm(formData)) return;

    sendData(formData)
      .then(() => {
        addTableRow(formData);
        clearFormInputs();
      })
      .catch(() => {
        alert('送信に失敗しました');
      });
  });

  // 入力データ取得
  function getFormData() {
    return {
      name: nameInput.value.trim(),
      age: ageInput.value.trim(),
      email: emailInput.value.trim(),
      phone: phoneInput.value.trim()
    };
  }

  // バリデーション
  function validateForm(data) {
    if (data.name === '') {
      nameError.textContent = '名前を入力してください';
      nameError.style.display = 'inline';
      setTimeout(() => { nameError.style.display = 'none'; }, 2000);
      return false;
    }

    if (!data.age || isNaN(data.age) || Number(data.age) <= 0) {
      alert('正しい年齢を入力してください');
      return false;
    }

    return true;
  }

  // バリデーションエラー初期化
  function clearErrors() {
    nameError.style.display = 'none';
    nameError.textContent = '';
  }

  // fetch送信
  function sendData(data) {
    return fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
  }

  // テーブルに行追加
  function addTableRow(data) {
    const row = document.createElement('tr');
    row.innerHTML = `
      <td>${data.name}</td>
      <td>${data.age}</td>
      <td>${data.email}</td>
      <td>${data.phone}</td>
    `;
    tableBody.appendChild(row);
  }

  // 入力欄クリア
  function clearFormInputs() {
    nameInput.value = '';
    ageInput.value = '';
    emailInput.value = '';
    phoneInput.value = '';
  }
});
</script>


</body>
</html>
```