RailsとJavaScriptを使った非同期テーブル更新の実装

この記事では、Railsアプリケーションで非同期的にテーブルを更新する方法について説明します。特に、スケジュール管理アプリケーションの例を用いて、特定の日付のスケジュールとその時間枠を動的に表示する方法を解説します。

## 1. 背景

スケジュール管理アプリケーションでは、ユーザーが日付を選択するたびに、該当日のスケジュールと時間枠を動的に表示する必要があります。この実装のためには、RailsサーバーからJSON形式でデータを取得し、フロントエンドでそのデータを使用してテーブルを更新する必要があります。

## 2. 実装の概要

### 2.1 サーバーサイド（Rails）

Railsのコントローラーで、選択された日付のスケジュールと時間枠を計算し、JSON形式で返すアクションを作成します。

### 2.2 クライアントサイド（JavaScript）

JavaScriptで、取得したJSONデータを使用してテーブルを動的に更新します。これには、テーブルのヘッダーとボディの両方を更新する処理が含まれます。

## 3. コードの詳細

### 3.1 Railsコントローラーの実装

まず、Railsのコントローラーで`update_slots`アクションを定義します。このアクションでは、選択された日付に基づいてスケジュールと時間枠を計算し、それをJSON形式で返します。

```ruby
# app/controllers/schedules_controller.rb

def update_slots
  date = params[:date]
  @date = Date.parse(date)
  @company = current_company
  @time_slots = generate_time_slots(@company)

  Rails.logger.info "Generated Time Slots: #{@time_slots}"

  @services = @company.services
  @service_availability = ScheduleService.calculate_availability(@company, @date, @services, @time_slots)
  
  response_data = {
    services: @services.map { |service|
      {
        name: service.name,
        duration: format_duration(service.duration),
        time_slots: @time_slots.map { |slot| { availability: @service_availability[service.id][slot] } }
      }
    },
    time_slots: @time_slots
  }
  
  Rails.logger.info "Response Data: #{response_data}"  # 追加されたログ出力
  render json: response_data
end
```

#### 解説

- `generate_time_slots`メソッドで時間枠を生成します。
- `calculate_availability`メソッドで、各サービスの可用性を計算します。
- `response_data`としてサービス情報と時間枠をJSON形式で構築し、レスポンスとして返します。

### 3.2 JavaScriptでのテーブル更新

次に、JavaScriptで取得したJSONデータを使ってテーブルを更新します。

```javascript
// app/javascript/controllers/application.js

function updateTable(data) {
  console.log("Updating table with data:", data);  // 受け取ったデータをログ出力

  const tableHead = document.querySelector('.table-responsive thead');
  const tableBody = document.querySelector('.table-responsive tbody');

  tableHead.innerHTML = '';  // ヘッダーの内容をクリア
  tableBody.innerHTML = '';  // ボディの内容をクリア

  const headerRow = document.createElement('tr');
  headerRow.innerHTML = `
    <th class="sticky-left">サービス</th>
    <th class="sticky-duration">所要時間</th>
  `;
  data.time_slots.forEach(slot => {
    const th = document.createElement('th');
    th.textContent = slot;
    headerRow.appendChild(th);
  });
  tableHead.appendChild(headerRow);

  data.services.forEach(service => {
    let row = document.createElement('tr');
    let nameCell = document.createElement('th');
    nameCell.classList.add('service-name', 'sticky-left');
    nameCell.textContent = service.name;
    row.appendChild(nameCell);

    let durationCell = document.createElement('th');
    durationCell.classList.add('service-duration', 'sticky-duration');
    durationCell.textContent = service.duration;
    row.appendChild(durationCell);

    service.time_slots.forEach(slot => {
      let slotCell = document.createElement('td');
      slotCell.textContent = slot.availability;  // 各時間枠の可用性を表示
      row.appendChild(slotCell);
    });

    tableBody.appendChild(row);
  });
}
```

#### 解説

- `updateTable`関数は、サーバーから取得したデータを使ってテーブルのヘッダーとボディを更新します。
- ヘッダー行を動的に生成し、時間枠を表示します。
- 各サービスの行を生成し、サービス名、所要時間、時間枠の可用性を表示します。

### 3.3 元のコードとの比較

#### 元のRailsコントローラーコード

```ruby
# app/controllers/schedules_controller.rb

def update_slots
  date = params[:date]
  @date = Date.parse(date)
  @company = current_company
  @services = @company.services
  @time_slots = generate_time_slots(@company)
  @service_availability = ScheduleService.calculate_availability(@company, @date, @services, @time_slots)

  render json: {
    services: @services.map do |service|
      {
        name: service.name,
        duration: format_duration(service.duration),
        time_slots: @time_slots.map do |slot|
          { availability: @service_availability[service.id][slot] }
        end
      end
    }
  }
end
```

#### 元のJavaScriptコード

```javascript
// app/javascript/controllers/application.js

function updateTable(data) {
  // ここでテーブルの内容を更新
  const tableBody = document.querySelector('.table-responsive tbody');
  tableBody.innerHTML = ''; // 現在の内容をクリア
  data.services.forEach(service => {
    let row = `<tr>
      <th class="service-name sticky-left">${service.name}</th>
      <th class="service-duration sticky-duration">${service.duration}</th>`;
    service.time_slots.forEach(slot => {
      row += `<td>${slot.availability}</td>`;
    });
    row += '</tr>';
    tableBody.innerHTML += row;
  });
}
```

### まとめ

この記事では、RailsとJavaScriptを使って非同期的にテーブルを更新する方法を紹介しました。サーバーサイドでデータをJSON形式で提供し、クライアントサイドでそのデータを使って動的にテーブルを更新する実装方法を詳しく解説しました。これにより、ユーザーが日付を変更するたびに、スケジュールと時間枠がリアルタイムで更新されるようになります。