前回の続きになります。
手順を履歴に保持します。
#履歴機能を作成
<head>
<!--<link rel="stylesheet" href="css/index.css" />-->
<!--<script src="js/index.js"></script>-->
<%= javascript_pack_tag 'tictactoes', 'data-turbolinks-track': 'reload' %>
</head>
<body id="tictactoes-body">
<%= form_with(url: "/tictactoes/sqClick", method: "post") do |f| %>
<div id="root">
<div class="game">
<div class="gmae-board">
<div>
<% @Squares.each.with_index do |sq, i| %>
<% if i % 3 == 0%>
<div class="board-row"></div>
<% end %>
<%= f.text_field '', :name => "item[]", :class => "square", :readonly => true , :value => sq.content%>
<% end %>
</div>
</div>
<div class="game-info">
<div><%= @nextStepMessage %></div>
<div>
<li><button type="button">Go to game start</button></li>
<% (0...@buttonCount).each do |num| %>
<li><button type="button">Go to move #<%= num + 1 %></button></li>
<% end %>
</div>
</div>
</div>
</div>
<%= f.hidden_field :clickButtonIndex, :value => @clickButtonIndex %>
<%= f.hidden_field :clickNo, :value => @clickNo %>
<%= f.hidden_field :stepNumber, :value => @stepNumber %>
<% end %>
</body>
履歴ボタンのところはサーバーサイドでボタンの数を埋め込む形としました。
実装中に@buttonCount
を定義し忘れることがあって、そのまま動かすとブラウザが固まって、CPUとメモリが上昇し続けるという状態になり、めっちゃハマりましたorz
PCの寿命かとも思いましたw
0...@変数
みたいな使い方したらダメなんでしょうね多分
※SCSSは前回と同様です。
(function() {
window.addEventListener("DOMContentLoaded", () => {
// 全square
document.querySelectorAll(".square").forEach((element, index) => {
// クリックイベント
element.addEventListener("click", squareClick.bind(element, index));
});
// 全button
document
.querySelectorAll(".game-info li > button")
.forEach((element, index) => {
// クリックイベント
element.addEventListener("click", historyButtonClick.bind(null, index));
});
let isClick = false;
function squareClick(index) {
// 2度押し防止、既に値が埋まっているか
if (isClick || this.value) {
return;
} else {
isClick = true;
}
document.getElementById("clickNo").value = index;
document.forms[0].submit();
}
function historyButtonClick(index) {
// 2度押し防止
if (isClick) {
return;
} else {
isClick = true;
}
document.getElementById("clickButtonIndex").value = index;
document.forms[0].action = "/tictactoes/hysBtClick";
document.forms[0].submit();
}
});
})();
js側はなんだかんだ結構記述しました・・・。
Rails.application.routes.draw do
get '/' => 'home#top'
get 'posts/index' => 'posts#index'
get 'top' => 'home#top'
get 'about' => 'home#about'
get 'tictactoes' => 'tictactoes#index'
get 'tictactoes/sqClick' => 'tictactoes#index'
get 'tictactoes/hysBtClick' => 'tictactoes#index'
post 'tictactoes/sqClick' => 'tictactoes#sqClick'
post 'tictactoes/hysBtClick' => 'tictactoes#hysBtClick'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
現状POSTした後のページでF5を押すと、CSRFチェック?に引っ掛かり、エラーになっていたのでルーティングを追加しました。
普通こんなやり方しないのでしょうか。
class TictactoesController < ApplicationController
#初期画面表示
def index
# 手順1以降を削除して初期化
Square.where.not(stepNumber: 0).delete_all
#手順0を全件取得
@Squares = Square.all
#手順の開始は0から
@stepNumber = 0
@clickButtonIndex = 0
@buttonCount = 0
#次の手番の文字列
@nextStepMessage = "次の手番: X"
end
#マス目クリック
def sqClick
#現在の手順インクリメント
stepNumber = params[:stepNumber].to_i
@stepNumber = stepNumber.succ
#マス目の配列
items = params[:item]
sq = Square.new
#既に勝敗が着いた
if sq.calculateWinner(items)
return
else
xIsNext = stepNumber % 2 == 0
clickButtonIndex = params[:clickButtonIndex]
#XとOを設定
items[params[:clickNo].to_i] = xIsNext ? "X": "O"
isWinner = sq.calculateWinner(items)
#現在より後ろの手順は削除
Square.where("stepNumber > ?", clickButtonIndex).delete_all
#新しい手順をDBに保存
sq.saveSquare(@stepNumber, items)
#次の手番の文字列更新
if isWinner
@nextStepMessage = "勝者:" + (xIsNext ? "X": "O")
else
@nextStepMessage = xIsNext ? "次の手番: O": "次の手番: X"
end
#最新の手順を取り直す
@Squares = Square.where(stepNumber: @stepNumber)
end
@buttonCount = @stepNumber
#indexのhtmlを使いまわす
render action: :index
end
#履歴ボタン押下
def hysBtClick
@clickButtonIndex = params[:clickButtonIndex].to_i
@Squares = Square.where(stepNumber: @clickButtonIndex)
#contentの配列を作る
items = @Squares.map { |s| s.content }
sq = Square.new
if sq.calculateWinner(items)
@nextStepMessage = "勝者:" + (@clickButtonIndex % 2 == 0 ? "O": "X")
else
@nextStepMessage = @clickButtonIndex % 2 == 0 ? "次の手番: X": "次の手番: O"
end
@stepNumber = @clickButtonIndex
@buttonCount = Square.count / 9 - 1
render action: :index
end
end
jsでやっていた時のようにxIsNext
変数は削除し、stepNumber
の余りで次の手順を判断するようにしました。
class Square < ApplicationRecord
Lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
]
def saveSquare(stepNumber, items)
#新しい手順のマス目保存処理
(1..9).each do |i|
Square.create(stepNumber: stepNumber, squareNumber: i, content: items[i - 1])
end
end
#勝敗判定関数(公式チュートリアルから拝借)
def calculateWinner(items)
Lines.each do |l|
a, b, c = l
if items[a] != "" && items[a] == items[b] && items[a] == items[c]
return true
end
end
return false
end
end
モデルはほぼ変更はないです。
ネットで調べると、今の時代for
はあまり使わないとのことなのでeach do
にしました。
なぜ使わないのかをちゃんと理解しないとな・・・
#感想
formをsubmitしているんですんごいもっさりします。
いずれajaxで組んでみようと思います。
その場合はクライアントにはVue.js
が使えたらいいなぁと思います。
Railsのお作法や知識が浅いだけかもしれませんが、今のところあまりRailsのメリットが感じられませんでした。
独特な記法すぎて、他のプログラミング言語と類似点が見つけにくく、習得しにくいし、習得しても他のプログラミングを覚えるときに役に立ちにくいと感じました。
これからも触っていっていつか本感想を振り返ってみたいと思います。