【Ruby on Rails】Ajaxで特定の要素を変更する

# はじめに
RailsのAjaxを使って特定の要素を編集・更新する方法について記載します。
例で簡単なカテゴリー一覧画面の１つのカテゴリーの名前を更新する処理を使います。

# バージョン
- ruby 2.6.3
- rails 5.2.1

# 画面概要
以下のような画面です。
![スクリーンショット 2020-03-28 22.13.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203478/489c6b4e-a966-b732-2827-0ed39b51b769.png)

この右のほうにある鉛筆マークの編集アイコンボタンを押すと、該当の行のカテゴリー名がフォームに変わります。
![スクリーンショット 2020-03-28 19.32.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203478/16f3c4ee-f645-cb25-f358-d14d88adea94.png)

カテゴリー名を変更し、「更新」を押すと、
![スクリーンショット 2020-03-28 19.33.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203478/37ec6089-0529-bc3c-6cc8-8f1da74181af.png)

変更後のカテゴリー名にデータが更新され、表示を非同期で変更します。
例では、「交通」→「交通費」に変更します。
![スクリーンショット 2020-03-28 22.13.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203478/cc7a1f96-d343-3ad9-caac-72c71fb7d561.png)



# 処理概要
1. 要素にAjaxのためのオプションを追加
link_toに`remote: true`オプションをつけます。

2. Ajaxするためのルーティングを追加

3. コントローラやモデルでオブジェクトの取得または更新

4. 追加したアクションのためのJavaScript用のviewファイルを用意し、画面の該当の要素を変更するためのJavaScriptを記載

# 実装前
実装前の主要なコード部分は以下になります。

```rb:app/controllers/categories_controller.rb
def index
  @categories = Category.where(user: current_user).order(:created_at)
end
```

```html:app/views/categories/index.html.erb
<ul class="todo-list" id="own-categories">
  <% @categories.each do |category| %>
    <li id="category-list-id-<%= category.id %>">
      <%= render 'a_category_list', category: category %>    
    </li>
  <% end  %>
</ul>
```

```html:app/views/categories/_a_category_list.html.erb
<span class="handle ui-sortable-handle">
  <i class="fa fa-ellipsis-v"></i>
  <i class="fa fa-ellipsis-v"></i>
</span>
<span class="text"><%= category.name %></span>
<%= category.common_mark %>
<div class="tools">
  <%= link_to edit_category_path(category), class: "text-redpepper space-left" do %>
    <i class="fa fa-lg fa-edit"></i>
  <% end %>
  <%= category.common_btn %>
</div>
```


# カテゴリー名を編集するフォームに変更するAjax処理の実装
鉛筆マークの編集アイコンボタンを押下して、カテゴリー名の要素をフォーム要素に変更する処理を記載していきます。
※注意：説明しないclass属性やid属性などがありますが、Ajax処理とは関係ありません。


### 1. Ajaxするためのオプションを追加
編集アイコンボタンにremote: trueを仕込む。

```html:app/views/categories/_a_category_list.html.erb
<%= link_to edit_category_path(category), remote: true, class: "text-redpepper space-left" do %>
  <i class="fa fa-lg fa-edit"></i>
<% end %>
```

### 2. Ajaxするためのルーティングを追加
Ajaxするときでも、ルーティングのresourcesメソッドが柔軟に対応してくれます。
ここでは、editアクションを追加します。

```rb:config/routes.rb
resources :categories, only: [:index, :edit]
```

### 3. コントローラでオブジェクトの取得
```rb:app/controllers/categories_controller.rb
def edit
  @category = Category.find(params[:id])
end
```

### 4. 追加したアクションのためのJavaScript用のviewファイルを用意し、画面の該当の要素を変更するためのJavaScriptを記載
editアクションなので、`app/views/categories/edit.js.erb`を作成します。

```javascript:app/views/categories/edit.js.erb
id = "<%= @category.id %>";
target = document.querySelector(`#category-list-id-${id}`);
html = "<%= j(render partial: 'form', locals: { category: @category }) %>";
target.innerHTML = html;
```

```html:app/views/categories/_form.html.erb
<%= form_with(model: category) do |form| %>
  <div class="row">
    <div class="col-xs-10">
      <div class="input-group input-group-sm">
        <%= form.text_field :name, value: category.name, required: true, class:"form-control", max: 15 %>
        <span class="input-group-btn">
          <%= form.submit submit_btn_letters, class: "btn btn-brown" %>
        </span>
      </div>
    </div>
    <div class="col-xs-1">
      <%= category.cancel_btn %>
    </div>
  </div>
<% end %>
```

これで、カテゴリー名を編集するためのフォームが表示できます。
![スクリーンショット 2020-03-28 19.32.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203478/16f3c4ee-f645-cb25-f358-d14d88adea94.png)


# カテゴリー名を更新するAjax処理の実装
編集フォームに変更できたので、次に、変更したい文字「交通費」に変更して、
更新ボタンを押下し、更新後のカテゴリー名を表示する処理を記載していきます。

### 1. Ajaxするためのオプションを追加
form_withに`remote: true`オプションを追加します。
(明示的に記載しなくてもデフォルトでremote: trueにはなっている。)

```html:app/views/categories/_form.html.erb
<%= form_with(model: category, remote: true) do |form| %>
  <!-- 省略 -->
<% end %>
```

### 2. Ajaxするためのルーティングを追加
updateアクションを追加

```rb:config/routes.rb
resources :categories, only[:index, :edit, :update]
```

### 3. コントローラでオブジェクトの更新
カテゴリー名を「交通費」で更新します。

```rb:app/controllers/categories_controller.rb
def update
  @category = Category.find(params[:id])
  @category.update(category_params)
end

private
def category_params
  params.require(:category).permit(:name, :is_common)
end
```

### 4. 追加したアクションのためのJavaScript用のviewファイルを用意し、画面の該当の要素を変更するためのJavaScriptを記載
編集フォームを更新したカテゴリー名に変更する処理を行います。
updateアクションなので`app/views/categories/update.js.erb`を作成します。

```javascript:app/views/categories/update.js.erb
id = "<%= @category.id %>";
target = document.querySelector(`#category-list-id-${id}`);
html = "<%= j(render partial: 'categories/a_category_list', locals: { category: @category }) %>";
target.innerHTML = html;
```

以上で、カテゴリー名を非同期で更新することができました。
![スクリーンショット 2020-03-28 22.13.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203478/67914c45-a0ef-b048-a66b-6d11d904f881.png)



# おわりに
JavaScriptでAjax処理を書いてもいいのですが、シンプルなAjaxはRailsであれば簡単にできます。
ただ、`〇〇.js.erb`の書き方は癖が強いので要注意です。