4
6

More than 3 years have passed since last update.

rails 自動更新

Last updated at Posted at 2019-11-26

ajaxを用いて自動更新機能を実装する。
今回は前回、作成した非同期通信のコードに書き加える形で自動更新を実装する。

完成品

Image from Gyazo

環境

ruby:2.5.1
rails:2.5.3
DB:mysql(Sequel Pro)
ブラウザ:Google
OS:Mac(10.14.6)

Github

作成する前に

なぜ必要か

日々使ってるLINEを考えてみましょう。
LINEのように、相手側が入力すると自分側が、LINEを開いたままでも、
リアルタイムで更新されます。あれも自動更新機能が実装されています。
自動更新がないままLINEのチャット機能を使用するとなると
表示されている画面をいちいちリロードしないと画面が更新されません。
これを一人ならまだしも、グループでチャットをするとなると面倒です。

自動更新の大きなメリットはリロードしなくてもリアルタイムでデータが更新されるという点です。

通信の流れ

実装に移る前に通信の流れを把握しておきましょう。
①ブラウザからindexアクションのリクエストが来たのでサーバーはレスポンスを返す
②ブラウザはサーバーからのレスポンスを解析
③解析して必要な全てのアセット (JavaScriptファイル、スタイルシート、画像) をサーバーから取得する
④JavaScriptを読み込む(指定の画面に移ったら、最新のデータがあったら取得、なかったら取得しない)
⑤json形式で返ってきた値をcontrollerで判別
⑥controllerからjson形式でリクエストが読まれjbuilderのファイルを読み込む
⑦jbuilderのハッシュの中にキーとバリューで値が保存されているのでJavaScriptに値(data)を渡す
⑧JavaScriptのファイルのdone、fail以下の記述が読まれ実行される
⑨ブラウザに表示される

同じ画面に居続けたら③から⑨を繰り返し自動更新が行われる

作成手順

ここでは自動更新機能を実装するだけの人用で書いていくが
一応データベースの情報はいじってないので自分の非同期通信を実装した方がいれば
「コード編集(ビュー)」の手順からファイルに追記していけば同じデータベースで自動更新機能が実装できる。

結論、データベースの名前が一律"ajax"になる。
もし「非同期通信」と「自動更新」のファイルを比較化をしたいって人は
データベースの名前を決めるファイル(database.yml)を編集してrails db:createしてください

 

Githubからコードをダウンロード

下記のURLからコードをダウンロード
https://github.com/tana1818/ajax
1.png

ダウンロードが完了したらファイル名を変更(なんでも良い)

任意のディレクトリにファイルうを配置したらファイル名変更
自分は自動更新なので’auto_update’って名前にしました。

 

gemのインストール、DBの作成、マイグレーション実行、サーバー起動

auto_update
bundle install #gemのインストール
rake db:create #DB作成
rake db:migrate  #マイグレーションファイルを実行する
rails s  #サーバーの起動

http://localhost:3000/
を検索

下図のように表示されたらOK
2.png

コード編集(ビュー)

①タイトルバーの名前変更

views/fruits/application.html.haml
!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    //下記記述変更
    %title Auto_update
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
  %body
    = yield

 
②indexにdata-id付与

views/fruits/index.html.haml
.container 
  .row
    %h1 Listing fruit
    - if flash[:notice]
      %p= flash[:notice]

  .row
    %table.table.table-bordered.table-sortable
      %thead
        %tr
          %th Name
          %th
          %th
          %th
      %tbody.fruit_item
        - @fruits.each do |fruit|
          //下記追加('item'クラスにデータのid情報追加)
          %tr.item{"data-id": "#{fruit.id}"}
            %td
              = fruit.name
            %td
              = link_to 'Show', fruit
            %td
              = link_to 'Edit', edit_fruit_path(fruit)
            %td
              = link_to 'Destroy', fruit, data: {confirm: 'Are you sure?'}, method: :delete

  .row
    %h1 New fruit
    = render 'form'

最新のfruitのidを取得するためにfruitテーブルのidを、HTMLの中に埋め込む必要がある。
ここで登場するのが「カスタムデータ属性」

カスタムデータ属性を使うときは、属性名を「data-」で始まる名称にする。
上記のように記述すれば、「data-id」という名前のカスタムデータ属性を設定できたことになる

このように「data-任意の名前」でカスタムデータ属性を設定しておくと
JavaScriptから簡単に値を取得できる

この記事がわかりやすいかも
https://wp.tech-style.info/archives/102

 

jsファイル編集

assets/javascripts/fruits.js
$(function(){
  //非同期通信
  function buildHTML(fruit){ //通信が成功するとdoneメソッドの引数にデータが入るようになっているため、これを利用してHTMLを組み立てる
    //5行目にdata-id = ${fruit.id}を追加
    var html = `<tr class = "item ui-sortable-handle" data-id = ${fruit.id}>
                  <td>
                    ${fruit.name}
                  </td>
                  <td>
                    <a href = /fruits/${fruit.id}>Show</a>
                  </td>
                  <td>
                    <a href = /fruits/${fruit.id}/edit>Edit</a>
                  </td>
                  <td>
                  <a data-confirm = "Are you sure?", rel = "nofollow", href = /fruits/${fruit.id}, data-method = "DELETE">Destroy</a>
                  </td>
                </tr>`
    return html;
  }
  $('#new_fruit').on('submit', function(e){ //'#new_fruit'の'submit'が押された時に発火
    e.preventDefault(); //これを書いてるせいで'submit'を押した際に要素にdisabledが付与される→最後の.alwasが書いてある行を足すことでそのdisabled要素を削除してる
    var formData = new FormData(this); //FormDataオブジェクトの引数はthisとなってる。イベントで設定したfunction内でthisを利用した場合はイベントが発生したDOM要素を指す。今回であればnew_commentというIDがついたフォームの情報を取得している
    $.ajax({
      url: "/fruits/", //ここはアクションのURLなのでrails routesで確認
      type: "POST", //ここはアクション名
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
    .done(function(data){ //非同期通信の結果として返ってくるデータは、done(function(data) { 処理 })の関数の引数で受け取る
    var html = buildHTML(data);
      $('.fruit_item').append(html) //htmlに追加
      // $('.b').val('') //なんかエラー出るなーと思ったらvarと書き間違えていた(ここでは'submit'を押した後テキストボックスに空の要素を付与してる)
      $('.new_fruit')[0].reset() //初期値があれば初期値にリセットされる
    })
    .fail(function(){ //エラーが置きた際
      alert('投稿できませんでした')
    });

    // .always(function(){ //この記述を書いてないと連続で投稿できない
    //   $(".c").removeAttr("disabled")
    // })
    return false;
    //要素の効果を無効化する、ちょっとわかんないけどとりま親要素のクリックをなかったことにするっぽいw
    //上記のコメント化記述は'disabled'を消しにいってる
    //こっちの方が記述量が少なくて良い
  })


  //自動更新
  if (location.pathname.match()){ //もし現在のURLパスがindexアクションだったら(http://localhost:3000/fruitsもしくはhttp://localhost:3000)
    setInterval(update, 5000);//5000ミリ秒ごとにupdateという関数を実行する
  }

  function update(){
    if($('.item')[0]){ //もし'.item'というクラスがあったら
      var fruit_id = $('.item:last').data('id'); //一番最後にある'.item'クラスの'id'というデータ属性を取得し、'fruit_id'という変数に代入
      $.ajax({
        url: location.href, //urlは現在のページを指定
        type: 'GET', //アクション名指定(データを表示させる)
        data: { id: fruit_id }, //rails に引き渡すデータ(これがparams[:id]になる)
        dataType: 'json'
      })
      .done(function(data){
        if (data.length){ //もしdataに値があったら
          $.each(data, function(i, data){ //'data'を'data'に代入してeachで回す
            var html = buildHTML(data);
            $('.fruit_item').append(html);
          })
        }
      })
      .fail(function(){ //そんなにこの記述はいらない気がするけど、異常系のエラー(途中で通信が中断されたり)が起きた時用
        alert('自動更新に失敗しました')
      });
    }
    else {
      clearInterval(); //値がなかったらsetIntervalを止める(この記述はなくても動く、別画面に遷移した際は、ブラウザを閉じた時同様、遷移した時点で遷移前のJavaScript実行は勝手に打ち切られる模様)
    }
  }
});

 

コントローラー編集(indexのみ)

controllers/fruites.controller.rb
  def index
    @fruits = Fruit.all
    @fruit = Fruit.new

    respond_to do |format| #記述追加
      format.html
      format.json { @new_fruit = Fruit.where('id > ?', params[:id]) } #jsのdataの情報がparams[:id]に入る
    end
  end

index.json.jbuilderのファイル編集

views/fruits/index.json.jbuilder
json.array! @new_fruit.each do |fruit|
  json.name fruit.name
  json.id fruit.id # 配列かつjson形式で@new_fruitを返す
end

 

以上で実装は終了
再度サーバーを立ち上げ直して

auto_update
rails s

うまく実装してできていれば上記のGIFのように自動で最新のデータが更新される

 
うまく実装できました?
実装できて自分の理解ができたら無駄なコメントは削除して読みやすいコードにしましょう。
(なんだか自分のコードにも_form.html.hamlに無駄なコメントアウトがあったりしたので、、)

今回でqiita2記事目なんですが、めちゃしんどいっすw
書いてみるとやってる事全然簡単に見えるのに、これあげるのにどれだけかかったか、、、
qiita人民の凄味を改めて実感しました。
ちょいまだまだ補足が書けていないのですが、一旦あげようと思います。
指摘事項あれば教えて頂けると嬉しいです。
それでは

はてなブログもやっているので是非!
https://tanagram18.hatenablog.com/

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6