質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

1997閲覧

動的に読み込んだDOM要素の表示完了時に処理を行いたい

knyk

総合スコア17

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2019/02/04 00:48

編集2019/02/04 23:40

前提・実現したいこと

Ruby on Railsで顧客管理などを行うシステムを作っています。
業務についてのメモを書き込む場があって,複数人が書き込むことになるので,自分がそのページを開いている間に他の人がメモを書き込んだら自動的に反映する(新しい書き込みがないかを定期的にサーバーにチェックしに行って,あった場合はajaxで部分更新する)という機能を実装中に,うまくいかずハマっています。

発生している問題

https://qiita.com/yuki-n/items/885f6f059167a7b5aedc
この記事を参考に,新しい書き込みをチェックして,あった場合は部分更新ということ自体は実現できました。
問題なのは,新しい書き込みがあった場合,その書き込みを一回だけ追加したいのですが,延々と無限に追加され続けてしまいます。
ajaxで新しく追加された部分が,最初に設定した「新しい書き込みをチェックする」機能を実現しているコードに反映されないため(最初は追加された部分は存在していなかったため),setIntervalでループしても同じ判断がずっとされてしまいます。

該当のソースコード

メインとなる部分と処理(index.html.erb。一部パーシャルになっており,その部分は後述)

idがtargetの子要素(classがupdate_businessnoteとなっているもの)の一番最初のもの(最新のもの)から,データidを取得して,サーバーにあるbusinessnoteのidと比較するということを一定時間ごとに行うことを予定しています

(略) <div id="target"> <%= render 'each_businessnote', bns: @businessnotes %> </div> (略) <script> $(function(){ $(function(){ if($('.update_businessnote')[0]){ var timer = setInterval(update, 10000); //10000ミリ秒ごとにupdateという関数を実行する } else { } }); function update(){ //この関数では以下のことを行う if($('.update_businessnote')[0]){ var businessnote_id = $('#target').children(0).data('id'); console.log("index.html"); $.ajax({ //ajax通信で以下のことを行う url: "businessnotes_controller/refresh_part", //controllerとactionを指定 data: { //railsに引き渡すデータは new_businessnotes: { id: businessnote_id } //このような形(paramsの形をしています) }, }); } else { //ない場合は clearInterval(timer); } } });

index.html.erbのパーシャル部分(_each_businessnote.html.erb)
各businessnote毎に表示するようになっています。

<% bns.each do |businessnote| %> <div class="field businessnote_<%= businessnote.id %> update_businessnote" data-id=<%= businessnote.id %>> (以下略)

コントローラ(businessnotes_controller.rb)

def refresh_part @legalcase = Legalcase.find(params[:legalcase_id]) @businessnotes = @legalcase.businessnotes.order('created_at DESC') respond_to do |format| format.js { @new_businessnotes = @businessnotes.where('id > ?', params[:new_businessnotes][:id]) } end end

コントローラーによって呼ばれるjavascript(refresh_part.js.rb)
classがupdate_businessnoteとなっているものの先頭に,最新のbusinessnoteを追加するということを予定しています

<% if @new_businessnotes.present? %> <% @new_businessnotes.each do |new_businessnote| %> console.log('refresh_part.js'); $('.update_businessnote').eq(0).prepend('<%=escape_javascript(render 'update_businessnote',businessnote:new_businessnote) %>'); <% end %> <%end%>

追加したい部分(_update_businessnote.html.erb)

<div class="field businessnote_<%= businessnote.id %> update_businessnote" data-id=<%= businessnote.id %>> (以下略)

試したこと

index.htmlのところに,以下の処理を追加しました。
やりたいこととしては,idがtargetの要素を監視して,子要素が追加された場合に一度setIntervalを止めて,
新たに追加された子要素が含まれた状態でdataのidを取得することです。
そうすれば,追加されたidとサーバーのidとの比較になるので,追加が止まるのではないかと考えました。

もっとも,そもそも子要素が追加されても,logに"add_update"も表示されませんし,setIntervalの解除もできていません。
(なので新しいsetIntervalもとりあえずは設定していません)

class update_businessnoteの要素が追加されたら検知したいのですが,現状ではどこがおかしいでしょうか?
またこの他のやり方でもいろいろ試しましたが,setIntervalを止めることができません。
新しいデータを取得したら一度setIntervalを止めたいのですが,どうすればいいでしょうか?
よろしくお願いします。

var target = document.getElementById('target'); console.log("mo.set") var mo = new MutationObserver(function (){ clearInterval(timer); console.log("add_update"); var businessnote_id = $('#target').children(0).data('id'); $.ajax({ //ajax通信で以下のことを行う url: "businessnotes_controller/refresh_part", //urlは現在のページを指定 data: { //railsに引き渡すデータは new_businessnotes: { id: businessnote_id } //このような形(paramsの形をしています) }, }); }); mo.observe(target,{childList: true});

補足情報(FW/ツールのバージョンなど)

ruby 2.4.1
rails 5.0.4

###最終的にどのように実装したか

回答欄でいただいたグローバル変数を使うというやり方で進めました。

途中うまくいかなかったのは,コントローラとjsの間でグローバル変数を受け渡せるrailsのgonというgemを使っていたのですが,ajaxで使うコントローラではどうやら変数の設定ができないようでした。
(console.logで確認すると,undefinedになってしまっていた)

改めてコードを見直してみると,refresh_part.js.erbではjsのコードとrubyのコードの両方を使うことができるので,そこで変数の受け渡しを実現できることがわかりました。

最終的に自分の目的を果たしたコードは以下のとおりです。
実際にはチャットそのものではなく,updateされたものが追加されると意図とは異なる挙動になるので,修正日時ではなくidを基準に新しいものがないかのチェックを行う仕様にしています。

コントローラ(businessnotes_controller.rb)

def index (略) gon.businessnote_id = @businessnotes.order('updated_at DESC').first.id end def refresh_part @legalcase = Legalcase.find(params[:legalcase_id]) @businessnotes = @legalcase.businessnotes.order('created_at DESC') @businessnote_refreshed = @businessnotes.order('updated_at DESC').first.id respond_to do |format| format.js { @new_businessnotes = @businessnotes.where('id > ?', params[:new_businessnotes][:id]) } end end

index.html.erb

<script> $(function(){ $(function(){ if($('.update_businessnote')[0]){ update_businessnote_id = gon.businessnote_id; var timer = setInterval(update, 10000); //10000ミリ秒ごとにupdateという関数を実行する } else { } }); function update(){ //この関数では以下のことを行う if($('.update_businessnote')[0]){ $.ajax({ //ajax通信で以下のことを行う url: "businessnotes_controller/refresh_part", data: { //railsに引き渡すデータは new_businessnotes: { id:update_businessnote_id } }, }); } else { //ない場合は } } }); </script>

refresh_part.js.erb

<% if @new_businessnotes.present? %> <% @new_businessnotes.each do |new_businessnote| %> update_businessnote_id = "<%=@businessnote_refreshed%>" $('.update_businessnote').eq(0).prepend('<%=escape_javascript(render 'update_businessnote',businessnote:new_businessnote) %>'); <% end %> <%end%>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

クライアント側で最終更新日時を保持しておき、
apiにその日時を投げてやってください
あとはapi側でクライアントの指定日時より新しいものを返すだけです
やってることはチャットですよね?

投稿2019/02/04 01:22

yambejp

総合スコア114769

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

knyk

2019/02/04 02:18

回答ありがとうございます! おっしゃるとおり,やりたいことはチャットと同じようなことです。 回答をいただいて,漠然とはわかりましたが,特にjavascriptについて知識が乏しいので,具体的にどうすればいいのかかがよくわかっていません。。 「クライアント側で最終更新日時を保持」とは,どのように実装するのでしょうか? 新しく追加したDOM要素のデータ(最終更新日時)も,当初の関数(上記のコードのupdate関数)に反映させることはできますでしょうか?
yambejp

2019/02/04 02:37

クライアント側のjsにlastmodified的なグローバル変数をつくっておくと よいでしょう。 もちろんタグに保持しても良いです(例えばinput hiddenとか) またajaxで渡すdataに当然、lastmodfiledのデータを盛り込み サーバー側がその日付を利用して前回以降に記載されたデータを 返す仕組みを作る必要があります
knyk

2019/02/04 08:08

ありがとうございます! たしかにおっしゃるとおりにすれば理屈の上では問題なく実現するはずというところまでは理解できました。 ただ,実装はできず,前に進んでいない状態です。。 グローバル変数に値を設定し,その後要素を追加時にその変数を更新してajaxの通信を行っているつもりですが,console.logで確認すると,変数はずっと同じ値です。 新しくDOM要素を追加した際にうまく変数に反映させることができていません。 可能であれば,現状のコードに即した実装のアドバイスをいただければ幸いです。
yambejp

2019/02/04 08:11

RDBに保持しているのですよね? そしてRDBへの登録や参照はできているのですよね? なにがわかってなにがわからないか、こちらはわかりません
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問