解決したいこと
Ruby on Railsで総当たりリーグ戦を実装しています。以下は、現在のフロントです。
ここに、対戦結果を非同期処理(tdをクリック)で記入して、データベースに保存できるように実装したいです。
現在できている実装
tdをクリックすると、非同期処理でそこに○が表示され、反対側に●が表示されるようになっています。
詰まっている実装
結果の記入は出来るのですが、ページをリロードすると○と●が全て消えてしまいます。そこで、データベースの中で更新(例:空白→○)するように実装したいです。
データベース設計
membersテーブル(10人)
・メンバーID
・メンバー名
・アカウント
・実績
・勝ち数
・負け数
・SB
resultsテーブル(1人に対して9人(10人))
・メンバーID
・対戦相手ID
・勝敗
現在のコード
ruby
1Rails.application.routes.draw do 2 root to: 'results#index' 3 get 'results/:id', to: 'results#win_or_lose' 4end
ruby
1class ResultsController < ApplicationController 2 3 def index 4 @members = Member.all 5 end 6 7 def win_or_lose 8 // 数字_数字を分解 9 resultAry = params[:id].split("_") 10 // 左側が勝利のtd 11 result_win = Result.find(resultAry[0]) 12 // 右側が敗北のtd 13 result_lose = Result.find(resultAry[1]) 14 15 if result_win.result == "○" 16 result_win.update(result: "") 17 result_lose.update(result: "") 18 else result_win.result == "" 19 result_win.update(result: "○") 20 result_lose.update(result: "●") 21 end 22 end 23 24end
html
1<div class="main"> 2 <div class="league-table"> 3 <h2 class="league-table-title">【リーグ表】</h2> 4 <table id="winorlose_table" border="2" bordercolor="black" style="border-collapse: collapse"> 5 <tr class="top-title"> 6 <th class="rank-title">順位</th> 7 <th class="player-title">対局者</th> 8 9 <% @members.each do |member| %> 10 <td class="opponent-name"><%= member.name %></td> 11 <% end %> 12 13 <th class="win-count-title">勝</th> 14 <th class="lose-count-title">負</th> 15 <th class="sb-count-title">SB</th> 16 </tr> 17 18 <% @members.each_with_index do |member, i| %> 19 <tr class="result-cells"> 20 <td id="rank_<%= i + 1 %>"></td> 21 <td class="self-name"><%= member.name %></td> 22 // 勝敗記入のtd 23 <td id="op_<%= i + 1 %>_1" data-id="<%= (i * 10) + 1 %>_<%= (10 * (i * 10 + 1)) - (100 * i + (9 - i)) %>"><%= Result.find((i * 10) + 1).result %></td> 24// 途中略 25 <td id="op_<%= i + 1 %>_10" data-id="<%= (i * 10) + 10 %>_<%= (10 * (i * 10 + 10)) - (100 * i + (9 - i)) %>"><%= Result.find((i * 10) + 10).result %></td> 26 // 勝敗のtd 27 <td id="win_<%= i + 1 %>">0</td> 28 <td id="lose_<%= i + 1 %>">0</td> 29 <td id="sb_<%= i + 1 %>">0</td> 30 </tr> 31 <% end %> 32 33 </table> 34 </div> 35</div>
css
1.header { 2 margin: 20px; 3 padding-bottom: 5px; 4 border-bottom: 2px solid lightgray; 5} 6 7.header span { 8 padding-left: 20px; 9 font-size: 25px; 10} 11 12.main { 13 margin: 30px; 14} 15 16.league-table { 17 margin-top: 20px; 18} 19 20.league-table-title { 21 margin-bottom: 5px; 22} 23 24.league-table td { 25 text-align: center; 26} 27 28.right_down_border { 29 background-image: linear-gradient(to top right, transparent, transparent 48%, black 49%, black 51%, transparent 52%, transparent); 30} 31 32.top-title { 33 height: 30px; 34} 35 36.rank-title { 37 width: 50px; 38 background-color: #FFFFAA; 39} 40 41.player-title { 42 width: 80px; 43 background-color: #EEEEEE; 44} 45 46.opponent-name { 47 width: 80px; 48 background-color: #EEEEEE; 49} 50 51.win-count-title { 52 width: 50px; 53 background-color: #FFD5EC; 54} 55 56.lose-count-title { 57 width: 50px; 58 background-color: #D9E5FF; 59} 60 61.sb-count-title { 62 width: 50px; 63 background-color: #F3FFD8; 64} 65 66.result-cells { 67 height:40px ; 68} 69 70.self-name { 71 height:40px ; 72 background-color: #EEEEEE; 73} 74 75.member-list { 76 margin-top: 30px; 77} 78 79.member-list-title { 80 margin-bottom: 5px; 81} 82 83.member-table td { 84 text-align: center; 85} 86 87.member-info { 88 height: 30px; 89} 90 91.member-title { 92 width: 100px; 93 text-align: center; 94 background-color: #EEEEEE; 95} 96 97.account-title { 98 width: 150px; 99 text-align: center; 100} 101 102.achievement-title { 103 width: 500px; 104 text-align: center; 105} 106 107.member-introduction td { 108 height: 30px; 109}
js
1// ※リーグ表の中身の定義 2var json_data = {"_1":{ 3 "member":"Aさん", 4 "_1":{"member":"Aさん","WinOrLose":9}, 5// 途中略 6 "_10":{"member":"Jさん","WinOrLose":0} 7 }, 8 9// 途中略 10 11 "_10":{ 12 "member":"Jさん", 13 "_1":{"member":"Aさん","WinOrLose":0}, 14// 途中略 15 "_10":{"member":"Jさん","WinOrLose":9} 16 } 17 }; 18 19// 初期処理 20function init(){ 21 var trs = document.getElementById("winorlose_table").getElementsByTagName('tr'); 22 // 1行目はヘッダーだから飛ばして 23 for(var itr = 1; itr < trs.length; itr++){ 24 var win = 0; 25 var lose = 0; 26 // テーブルの行毎に処理 27 var tds = trs[itr].getElementsByTagName('td'); 28 for(var itd = 0; itd < tds.length; itd++){ 29 if(tds[itd].id.indexOf('op_') != -1){ // idにop_を含む(勝敗記入のtd) 30 // 勝ち負け入力<td>タグの場合 31 var arr = tds[itd].id.split('_'); // op_数字_数字 → [op, 数字, 数字] 32 if(arr[1] == arr[2]){ 33 // 対戦が同じ場合 34 tds[itd].classList.add('right_down_border'); // 縦横同値マスにクラスを指定 35 } 36 else { 37 // 勝ち数を加算 38 if(tds[itd].innerHTML == '○'){ 39 win ++; 40 } 41 // 負け数を加算 42 else if(tds[itd].innerHTML == '●'){ 43 lose ++; 44 } 45 // 対戦が違う場合、イベントを登録 46 // onclick="winorlose_click(this);" と同じ内容 47 tds[itd].addEventListener('click', {name: this, handleEvent: winorlose_click}); 48 if(json_data['_'+ arr[1]]['_'+ arr[2]]["WinOrLose"] == 1){ 49 tds[itd].innerHTML = "○"; 50 } 51 else if(json_data['_'+ arr[1]]['_'+ arr[2]]["WinOrLose"] == -1) { 52 tds[itd].innerHTML = "●"; 53 } 54 } 55 } 56 else if(tds[itd].id.indexOf('win_') != -1) { 57 // 勝ち表示 58 tds[itd].innerHTML = win; 59 } 60 else if(tds[itd].id.indexOf('lose_') != -1) { 61 // 負け表示 62 tds[itd].innerHTML = lose; 63 } 64 } 65 } 66} 67 68// クリックしたときの処理 69var send_winorlose = false; 70var XHR; 71function winorlose_click(el){ 72 if(send_winorlose) return; // 送信中は実行できない 73 send_winorlose = true; // 処理中とする 74 75 // el にはどこでクリックされたかの情報が入っている 76 var arr = el.target.id.split('_'); // idを'_'で分ける .targetで親要素イベントも発火 77 var oppid = arr[0] + '_' + arr[2] + '_'+ arr[1]; // 反対側のid 78 // 勝敗の記入と取消 79 if(el.target.innerHTML == ''){ // 勝敗を記入 80 el.target.innerHTML = '○'; // 勝ち 81 document.getElementById(oppid).innerHTML = '●'; // 負け 82 json_data['_'+ arr[1]]['_'+ arr[2]]["WinOrLose"] = 1; //jsonのデータ自体を更新 83 json_data['_'+ arr[2]]['_'+ arr[1]]["WinOrLose"] = -1; //jsonのデータ自体を更新 84 } 85 else if(el.target.innerHTML == '○'){ // 勝敗を取消 86 el.target.innerHTML = ''; // 空白 87 document.getElementById(oppid).innerHTML = ''; // 空白 88 json_data['_'+ arr[1]]['_'+ arr[2]]["WinOrLose"] = 0; //jsonのデータ自体を更新 89 json_data['_'+ arr[2]]['_'+ arr[1]]["WinOrLose"] = 0; //jsonのデータ自体を更新 90 } 91 92 // 勝ち数と負け数の計算 93 var trs = document.getElementById("winorlose_table").getElementsByTagName('tr'); 94 var win = 0; 95 var lose = 0; 96 // 1行目はヘッダーだから飛ばして 97 for(var itr = 1; itr < trs.length; itr++){ 98 // テーブルの行毎に処理 99 win = 0; 100 lose = 0; 101 var tds = trs[itr].getElementsByTagName('td'); 102 for(var itd = 0; itd < tds.length; itd++){ 103 if(tds[itd].id.indexOf('op_') != -1){ 104 // 勝ち負け数カウント 105 if(tds[itd].innerHTML == '○'){ 106 win ++; 107 } else if(tds[itd].innerHTML == '●'){ 108 lose ++; 109 } 110 } else if(tds[itd].id.indexOf('win_') != -1){ 111 // 勝ち表示 112 tds[itd].innerHTML = win; 113 } else if(tds[itd].id.indexOf('lose_') != -1){ 114 // 負け表示 115 tds[itd].innerHTML = lose; 116 } 117 } 118 } 119 120 //※ データの送信は処理の最後 121 //※ 非同期でも結果が返ってくるまでクリックを無視するように変更 122 let resultId = el.target.getAttribute("data-id"); 123 if(!XHR){ 124 XHR = new XMLHttpRequest(); 125 XHR.responseType = "json"; 126 XHR.onload = function() { 127 send_winorlose = false; // 送信完了 128 }; 129 XHR.onerror = function() { 130 send_winorlose = false; // 送信完了 131 }; 132 } 133 XHR.open("GET", `/results/${resultId}`, true); 134 XHR.send(); 135} 136 137window.addEventListener("load", init);
調べた内容
・[Rails] Ajaxを用いて非同期通信でチャットメッセージを送る
https://qiita.com/kumasuke/items/36365bcdf30eaea65250
チャットのようにメッセージを非同期で入力するには、Ajaxを使うとありましたが、クリックすると既存のtdに○を表示することに応用を利かすのが難しいです。
tdをクリックした際の処理を実装する
クリックしたtdに○が付いて、反対側のtdに●が付く。
→データベースのresult.op_1からresult.op_10までのいずれかが、空白から○に更新される。
Ajaxで以上の実装をするにあたって、参考となる記述や参考を教えていただけると助かります。よろしくお願いします。