前提・実現したいこと
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%>
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/02/04 02:18
2019/02/04 02:37
2019/02/04 08:08
2019/02/04 08:11