Ruby
JavaScript
jQuery
Ajax
RubyOnRails

【Rails5】Ajaxでビューに要素を追加する方法と、それに対してjQueryでイベントハンドラを設定する方法

概要

以下の項目について、Rails初心者の私が躓いた部分がいくつかあったので、
備忘録を兼ねて、シンプルなコードを例に知見を共有します。

  • Rails5系でAjaxを使用する方法
  • 動的に生成したDOM要素に対してjQueryでイベントハンドラを設定する方法

目標とする動作

824cb2b7a55ed598b5cb1363455f3dc6.gif

Rails5系でAjaxを使用する方法

先程のgifでいうところの、各ニュースタイトルの横にある『あとで読む』ボタン押下後、
『あとで読むリスト』にその項目をAjaxで追加する部分について解説します。

ビュー

こちらが、ビューのソースコードです。
(erbはQiita側のシンタックスハイライトが上手く効かなかった:sob:)

home.html.erb
    #読売新聞の記事一覧を表示するテーブル。
        #@all_newsには最新のニュースに関する全てのレコードが入っている。
         <table class="latest_news_table">
      <thead>
        <tr>
          <th>タイトル</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <% @all_news.each do |news| %>
        <tr>
          <td><%= link_to  news.title, news.link %></td>
          #あとで読むボタンには、各ニュースに対応したidが付与されている
          <td><span class="btn btnAtode" id=<%= news.id %>>あとで読む</span></td>
        </tr>
        <% end %>
      </tbody>
    </table>

    #あとで読む記事を表示するテーブル。
    #all_atodesにはあとで読む予定のニュース記事に関する全てのレコードが入っている。
        <table class="atode_news_table">
      <thead>
        <tr>
          <th>タイトル</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <% @all_atodes.each do |atode| %>
        <tr>
          <td><%= link_to  atode.title, atode.link %></td>
          <td><span class="btn btnDokuryo" id=<%= atode.id %> method: :delete>読了</span></td>
        </tr>
        <% end %>
      </tbody>
    </table>

Ajax処理を行うjsファイル

app/assets/javascripts配下に作成しました。

Ajax.js
$(function () {
   //あとで読むボタンに対するイベントハンドラ
   $(".btnAtode").click(function() {
    //ボタンのidを取得し、後述するatodeアクションに渡す。
    return $.ajax({
      url: 'ajax/atode',//routes.rbの記述で、このurlとコントローラーのアクションを対応付ける
      type: 'post',
      data: { set_id: $(this).attr('id') },//アクションに渡したいデータ
      dataType: 'json',
      async: true,
    //コントローラーから無事にjsonが帰ってきた際の動作
    }).done(function(response) {
      //atodeアクション終了後の動作。あとで読むリストに項目を追加
      //response[0]にニュースのタイトル、[1]にurl、[2]にidが格納されている
      $(".atode_news_table").append(
        "<tr><td><a href=" 
                    + response[1] + ">" + response[0]
          + "</a></td><td><span class='btn btnDokuryo' id="
          + response[2] + " method: :delete>読了</span></td></tr>"
      )
    //コントローラーからjsonが帰ってこなかった時の動作
    }).fail(function() {
        alert("失敗しました");
    });
  });
 });

コントローラー

あとで読むボタンのクリックに対応したアクションです。

home_controller.rb
  def atode
        #あとで読むボタンが押された場合、ボタンに付与されたidをもとにNewsテーブルからニュースを取得
    @cheked_news = News.find(params[:set_id])
    #取得したニュースと同じ内容をAtodeテーブルに記録
    @atode_news = Atode.create(source: @cheked_news.source, title: @cheked_news.title, 
                                link: @cheked_news.link, checked: true)
    #各値をjsonに格納して、ajax.jsに返却 ajax.jsではresponse[0]〜[2]として扱われる
    render json: [@cheked_news.title, @cheked_news.link, @atode_news.id]
  end

動的に生成したDOM要素に対して、jQueryでイベントハンドラを設定する方法

先程のgifでいうところの、Ajaxで『あとで読むリスト』に追加された項目の『読了』ボタンを押すと
記事が消えていく動作について解説します。

方法としてはajax.jsの『あとで読むリスト』に各値を追加
イベントハンドラを設定するだけです。

以下、先程のajas.jsに一部加筆

Ajax.js
//略
}).done(function(response) {
      $("#atode-table").append(
        "<tr>" +
          "<td><a href=" + response[1] + ">" + response[0]
          + "</a></td>"
          + "<td><span class='btn btn-info btn-sm btnDokuryo' id="
          + response[2] + " method: :delete>読了</span></td></tr>"
      )
      //以下加筆箇所
            //読了ボタンに対するイベントハンドラ
      $(".btnDokuryo").click(function(){
        let set_id = Number( $(this).attr('id') )
        return $.ajax({
          url: 'ajax/done',
          type: 'delete',
          data: { set_id },
          dataType: 'json',
          async: true,
        //response[0]に削除したい項目のidが格納されている
        }).done(function(response) {
          //あとで読むリストの項目を削除
          var done_article = $("#" + response[0]).closest("tr").remove();
          $(done_article).remove();
        }).fail(function() {
        });
      })
//略

まとめ

Ajaxの使い方については、シンプルなコードを載せてみました。
必要箇所をコピペしてお使い下さい。

また、Ajaxで動的に生成したDOM要素に対しては、
その要素を追加にイベントハンドラを設定する所がポイントです。
私は別のjsファイルを作成し、そちらでイベントハンドラを設定していましたが、
Ajaxで追加した項目についてはclickイベントが拾えず悩んでしまいました...。
同様の悩みを抱えている方の助けになれば幸いです。