【Railsでアプリ開発中】プログラミング初心者の勉強ブログ #37
こんにちは。
37日目。Rubyでeachを実行した際の各要素にインデックス番号を振り、動的に扱う方法をやっていきます。
はじめに
この記事は、railsフレームワークでオリジナルwebアプリ「免許学科試験学習サイト」作成中の僕が、プログラミングをしている中で気づいたことや学んだことを書いております。プログラミング初心者なので知識は少ないですが、現在通っているプログラミングスクール「DIVE INTO CODE」で学んでいることや、ネットで見つけた様々な記事を参考に記事を作成しております。
目次
目次
前回までの内容
前回の記事(【HTML】セレクトボックスの使い方(selectタグの書き方、JavaScriptで要素取得))では、免許アプリの「一問一答機能」を拡張し、セレクトボックスを用いて「問題数」と「出題範囲」を絞れるようにしました。セレクトボックスについて1から使い方を解説した記事になります。
今回は「問題一覧」ページを編集。「答えを確認」をクリックすると解答と解説が現れるようにするため、eachを動的に処理できる「each_with_index」メソッドを使用していきます。
each_with_indexメソッドでeachを動的に使う
eachを動的に利用するメリット
Railsでrubyを書いていくにあたって、「each文の繰り返しを用いたデータの表示をしたい」ってなる時が結構頻繁にあります。
最近JavaScriptをrailsで書いてると、このeach文に悩まされることが多いのです。
例えば、
@questions = Question.all
@questions.each{ |question| }とすれば、Questionモデル内のデータを全て取り出して表示することができます。大変便利です。
しかし、このように書くと、データ1つずつに何か動きや見た目を変えたいときに、
全部データがまとまっててどう区別つければいいか、いつも悩んでいました。
この悩みを解決してくれたのが「each_with_index」メソッドです。今回はこちらを使い JavaScriptで表示の動きを一つのデータごとに付けていきます。
こうしてみると大したことないように見えますが、
問題ごとにJavaScriptが対応しております。この動きをさせたかったんです。
indexをつけないと「答えを確認」クリック時、1つずつではなく、全ての問題の答えが一遍に表示されてしまいます。
(〇〇.each_with_index)実際のコーディングと仕組み
コード部分
上の画像の実際のコードです。
views/questions/devide.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<% @devide_questions.each_with_index do |question, index| %> #eachにindexをつける <div id="devide_question_div_<%= index %>" class="devide_question_div"> #rubyでid部分に直接indexを書き込む <h2>問題</h2> <p><%= question.content %></p> </div> <button id="answer_open_<%= index %>">答えを確認</button> #rubyでid部分に直接indexを書き込む <div id="devide_answer_div_<%= index %>" class="devide_answer_div"> #rubyでid部分に直接indexを書き込む <button id="answer_close_<%= index %>">閉じる</button> #rubyでid部分に直接indexを書き込む <p>答え: <% kaitou = "◯" %> <% if question.answer == false %> <% kaitou = "×" %> <% end %> <%= kaitou %> </p> <p>解説:<%= question.explanation %></p> </div> <br><br><br> <% end %> |
views/questions/devide.html.erb(scriptタグ内)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<script> $(function(){ var i = 0; while(i < gon.devide_questions.length){ const index = i; $('#answer_open_' + index).click(function(){ $(this).hide(); $('#devide_answer_div_' + index).fadeIn(); }); i++; }; #「答えを確認」をクリック時のアクション定義 #各インデックスごとにクリック時のアクションを生成。問題の数分繰り返す。 #セレクタにも変数を毎回与え、それぞれのidを持つdivに対応したクリックアクションにする。 #定数indexを定義し、繰り返すごとに増えてしまうiを固めておく。(重要) }); $(function(){ var i = 0; while(i < gon.devide_questions.length){ const index = i; $('#answer_close_' + index).click(function(){ $('#devide_answer_div_' + index).hide(); $('#answer_open_' + index).fadeIn(); }); i++; }; #「閉じる」ボタンクリックアクションの定義 #基本的に上と同じ }); </script> |
controllers/questions_controller
1 2 3 4 5 6 7 8 |
class QuestionsController < ApplicationController #省略 def devide @devide_questions = Question.where(field_id: params[:id]) gon.devide_questions = @devide_questions #gonの利用 end #省略 end |
仕組みと解説
以下のコードが基本となります。
1 2 3 |
<% @questions.each_with_index do |question, index| %> #繰り返し内容 <% end %> |
こうすることで「index」にインデックス番号が入ります。
このときindexは0から始まります。(@questions配列の一つ目がquestionに代入され、処理が行われる際「index = 0」で自動定義されてます。)
1 2 3 4 |
<div id="devide_question_div_<%= index %>" class="devide_question_div"> <h2>問題</h2> # 省略 </div> |
divに設けたidは、直接rubyを書き込んでindexをつけてやります。
こうすることで、devide_question_div_0、devide_question_div_1・・・
のようなidを持ったdivが問題数分生成されます。
次がscriptです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<script> $(function(){ var i = 0; while(i < gon.devide_questions.length){ const index = i; $('#answer_open_' + index).click(function(){ $(this).hide(); $('#devide_answer_div_' + index).fadeIn(); }); i++; }; #「答えを確認」をクリック時のアクション定義 #各インデックスごとにクリック時のアクションを生成。問題の数分繰り返す。 #セレクタにも変数を毎回与え、それぞれのidを持つdivに対応したクリックアクションにする。 #定数indexを定義し、繰り返すごとに増えてしまうiを固めておく。(重要) }); # 省略 </script> |
先ほど解説した、rubyで生成されている
devide_question_div_0、devide_question_div_1・・・
のように連続したidがついたdivそれぞれに、クリック時のfanctionを定義するため、
こちらでも繰り返し文whileを使います。
このとき重要なこと。それは、
「(const index = i ;)で定数indexを定義し、繰り返すごとに増えてしまうiを固めておく」
ことです。
実は詰まったのがここでした。
「 i 」はdevide.html.erbが呼び出され、scriptタグ内のwhileが起動し、表示された段階で
「最後の問題のdivにつけられたインデックス番号」に変わってしまう点。
つまり、最初のwhile起動時の「i」と、
実際にユーザーが「答えを確認」ボタンをクリックした時の「i」が異なるということ。
すると、「答えを確認」ボタンを押しても「devide_answer_div」クラスがつけられたdivのインデックスが対応しなくなり、答えが確認できなくなります。
なので一旦iを保存しておく器として、indexを用意します。
これでうまいこと表示されます。
今は質素です。
今度cssつけてリッチにさせます。
まとめ
今回はeachを動的に使い、各要素に対しそれぞれJavaScriptでアクションを定義しました。
悩みが一個解消されました。よかったです。
JSで変数を用いて処理を行う場合、「表示」のタイミングと、そのときの「変数の中身」を確認する必要があるなと。
ajaxもそうですが、定義の仕方とタイミングの違いで、だいたい上手く行ってないことが多いです。
「動的」という言葉も今までしっくりきてなかったので使えなかったのですが、
今回の勉強でやっと理解できました。
定数も今まで使ったことなかったですし、いつ使うんだよって思ってたというか、
定数の存在自体忘れかけていました。貴重なアウトプットいただきました。
まあこんなところです。
以上ありがとうございました。