モデルの中の要素をランダムに表示したい(3)

昨日の続き

現状、

f:id:mst_kb:20210506191159j:plain

これの外枠は設定したから

まずは、クリックしたら表示が消えるとかを設定する。

function count (){
const shuffleQue = document.getElementById("shuffle_que");
shuffleQue.addEventListener("click", (e) => {
e.preventDefault();
// id順のリストを見えなくし、ランダム順のリストを表示する
const idListDisplay = document.getElementById("id_result");
idListDisplay.setAttribute("style", "display:none;")
const randListDisplay = document.getElementById("rand_result");
randListDisplay.setAttribute("style", "display:block;")

こんな感じだろうか、

なんか、うまく行かない。

ランダムの一覧の方が表示されない

id順のやつが消えるだけだ

shuffle.js:9 Uncaught TypeError: Cannot read property 'setAttribute' of null

エラーが出ている。

 

https://sbfl.net/blog/2019/02/01/javascript-error-messages/

setAttributeの中身が空。ということは

randListDisplayが上のidListDisplayみたいに上手く取得できていないということなので、誤字かな?

目を皿にして確認したが、違うみたい。

なんか2回連続で実行するとダメとかかな?

id順のリストを消す方の処理をコメントアウトで実行してみるけど同じエラー

 

id順の方の消す処理はできているのに、したの方だけできていないのが全く意味がわからない。

 

cssの方は適用されているから、idがおかしいということもないだろうに、それに記述方法はid順のリストの方と同じだ。

(↑は間違い、CSSはclass名で通っているから違う)

他に違いはないか

命令文がnoneとblockかの違いだ、noneの方ならできるのだろうか

できなかった。

確かにエラー文はそこをエラーだと言っていない。

 

ものすごい時間がかかってしまったが原因が判明した。

<div class = 'rand_result', id = 'rand-result'>
<div class = 'rand_result', id = 'rand-result'>

上から下に変更。

idの前のスペースが全角だったorz

 

VScodeで全角スペースが入っていると白く表示されるようにしたはずだけど。。。

今一度確認しにいく、

zenkakuという拡張機能

再インストールして、再起動してみたらちゃんと機能した。

いつの間にか適用されなくなっていたみたい。

でもこれで、同じミスは防げるはず。

 

気を取り直して

肝心の中身をjavascript(以下js)でモデルの中身を表示していくところを考える。

 

何やら上手くいかない。

そしてjs上でランダムに並び変わっていてもcategory_idなどidで取得しているものがidのまま表示されてしまう。

 

そこで、jsで隠したり表示したりはできているんだから、モデルの中身シャッフルはjs上でやらなくていいのではと思い至る。

 

とりあえず、1度はランダムのものか、そうでないかの表示はできた。

 

しかし、ランダムで表示されたものがあまりランダムされていないなど、複数回並び替えたいこともあるだろう。

 

そこで

f:id:mst_kb:20210507185028j:plain

こうする。

jsでいじる必要のない並び順のものを隠すようすれば良い。

(jsへの理解を深めるための処置が必要なのを再認識)

 

これで当初予定していた機能の実装その1の目処はたった。

 

モデルの中の要素をランダムに表示したい(2)

前回の続き、

まずは、前回に引き続きjavaに result を渡す方法を調べる

ruby javascript 変数 受け渡し」で検索

https://qiita.com/Kohei_Kishimoto0214/items/d919b00d75dec0699cf0

こちらの通りにやってみることに

 

①viewに変数を埋め込む

サンプルのコードがhaml記法になっているからhtmlに直してと書いてある

haml記法を調べて、直してみる

https://qiita.com/49497974m/items/d94245a12db4f60b7a11

%input{name: "result", type: "hidden", value: @results, class: 'result' }/
<input name= "result", type= "hidden", value: @results, class='result' >

上から下に変更してみる

とりあえず表示はされている。

 

javascriptに読み込ませる

result = $('.result').val();

これで@resultを読めているらしい

それで

result = result.shuffle

を続けて入力してみるが

文法エラーは起きないけど、シャッフルもされていない。

そこで、追加したものをコメントアウトして動作を確認していったら、

どうやら、上の二つのどちらかが仕事をしていなそう。

よって、resultに中身が入っていないか、shuffleメソッドがjavaで使えないかだと思われる。

まずはresultの中身を確認する

consoleを開いたら

shuffle.js:5 Uncaught ReferenceError: $ is not defined
at HTMLAnchorElement.<anonymous> (shuffle.js:5)

とある。

これは5行目がおかしいということだろうから5行目を確認。

result = $('.result').val();

5行目は上記。

https://www.genius-web.co.jp/blog/web-programming/points-to-check-on-javascript-errors.html

こちらのサイトによれば、変数名「:$」が読み込めない。という状況になっているそう。

jQueryを使って読み込んでいるとのことなので、

jQuery記述の仕方を確認してみる。

 

そもそもjQueryがなんだかわかっていなかった

yarn add jquery

まずは導入

config/webpack/environment.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    jquery: 'jquery',
  })
)

module.exports = environment
app/javascript/packs/application.js
1
2
3
4
5
6
7
8
9
// 省略

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require('jquery')

// 省略

と導入したら先程のエラーは吐かなくなった。

 

ただページ更新はうまくいったが

シャッフルはできない。

またresultが入っていないのか

console.log(result)

をしてみるが、何も出てこない

今の記述に

window.location.reload();

が入っているから、更新されてログが残っていないよう

一旦コメントアウトする。

それでも何も出てこない。

 

切り口が思いつかないので、また少し検索してみる

https://techacademy.jp/magazine/9444#sec2

何やら配列はいじれないと書いてある。

違う切り口にしてみようか

 

https://qiita.com/mosa_siru/items/e69eee47a183b13cfb62

こちらのサイトにある Gon gem というのを使ってみる

ただ、使用例がないので、どのように適用したものか

そこで、リンク先にいってみる

http://railscasts.com/episodes/324-passing-data-to-javascript?language=ja&view=asciicast

 

このサイトの最初に

ここでおこないたいのは、JavaScriptの機能を使って商品データを取得して表示する処理です。そのためにはアプリケーションからクライアントで実行されているJavaScriptに情報を渡す必要があります。

とある。これはやりたいことと似通っている。

gonの説明を読む限り、コントローラーを

ントローラアクションのgonオブジェクトに変数を設定します。

/app/controllers/products_controller.rb
class ProductsController < ApplicationController
  def index
    gon.products = Product.limit(10)
  end
end

とあるので、

def search
@results = @p.result.includes(:category)
gon.products = @results
end

としてみる。

そして

console.log(gon.products);

とすると

  1. (19) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    1. length19

これは・・・できているのでは!

ようやく進んだので、とても嬉しい。

 

次はこの検索結果をクリックしたら並び順をshuffleする

Viewでやっていたようにとりあえずやってみる

gon.products.shuffle;
console.log(gon.products);

当然のごとくできない。

よって、

javascriptで配列の並びをランダムに置き換わるものがないかを検索する。

https://www.sejuku.net/blog/62904

キーを使ったsort()を使えば、id順に並び替えるはできそう。

後から使うが、今はランダムに置き換わる方針を探す

 

http://bashalog.c-brains.jp/14/03/05-100000.php

こちらにランダムで並び替える記述があるので使わさせていただく

  1. (19) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    1. length19

並び変わっている!

 

お次は、この並び変わったやつをviewファイルに戻す

クリックしたら

「gon.products」の中身は変わっているが、

今、viewファイルにあるのは「@result」。

 

ただ、今はreloadを最後にして表示し直している。

(「gon.products」の中身を「@result」に入れられたとして)

そうすると

javaでいじった変数を渡す

②reloadして表示し直す

③渡された@resultはコントローラーの処理に従って元のid順に戻る。

となってしまう。

 

よって、データを挿入していくパターンならできるか、

表示されているのを全部削除して、

今得た、「gon.products」の中身を並べる記述をjava上で行うのはどうだろうか。

 

もしくは

今はid順に並んでいるのが表示されているので、

クリックしたらそれが隠されて、

ランダムなやつが表示されるようになるとかはどうか

 

とりあえず表示できるようにしてみる。https://tech-master.s3.amazonaws.com/uploads/curriculums//b10b4adeebb73bd73cc0ff2797b09ebd.png

こんな感じで「ランダムで並び替え」を押したら

現状並んでいる、id順の検索結果を潰せるように潰せるようにできないか

 

(現状、ランダムかそうでないかだけなのでこれが実装できればこれでいいのだろうが、将来的には複数条件でソート可能にしたい。

RubyonRails:検索条件を引き継いで検索結果のソート順を変更する - Madogiwa Blog

こんな感じの表示にしたい。ただ、今回はjsの経験を積むためにも表示非表示でできないかを試してみる)

 

f:id:mst_kb:20210506191159j:plain

こんなイメージ

とりあえずdivの記述はできた。

次は記述を行なっていく。

 

モデルの中の要素をランダムに表示したい(1)

カリキュラムの復習を一旦終了し、オリジナルアプリの実装(1から自分の考えた構想通りのものを作れるか)をやってみることに。

 

まずは、

タグの検索機能でしたように、自分が指定した条件で問題が表示できる機能を実装したい。

これだけなら、新しい知識は必要なさそうだが、

問題なので、順番に答えを覚えてしまうのが怖い。条件が厳しく、問題数が少ないとあればなおさら答えを覚えてしまい勉強になっているようでなっていないという事態になりやすい。

 

そこで、検索ごとにランダムに並び変わるシステムが欲しい。

また、これは後回しになるが

ランダム出力かそうでないかを選択できる。

一覧を印刷できる。

一覧をそのままWebで解答できる。

 ⇨Javaを使って次のページに移行しなくても◯か❌か表示したい。

 ⇨いちいち検索しなくてもその場で並び替えられるシステムを組みたい。

一覧をエクセルファイルで出力できる。

 

まだしたいことはあるが、とりあえずここまでできるものを作ろうと思う。

 

話を戻して、

 

まずは、モデルに登録された問題をランダムに表示するようにする機能を作りたい。

知識はないので、どのように設定すればできるかを考える。

・問題を登録するカラムに「ランダムな値」を登録する。

 →カラムがランダムなら知っている知識、orderメソッドでランダムに並び変わるはず。

 →毎回変わるような乱数をそもそもモデルに登録できるのか?

 →ランダムな値を登録できたとして、そのランダムな値に固定されてしまい、毎回並び順が変わるということはないのではないか。

 

・とりあえず検索してみる

Ruby カラム ランダム」で検索。

rand()で乱数が作られるのはカリキュラムでも読んだ。

しかし、これでは懸念通り最初だけランダムな値が出力されてしまう。

バラバラに出力はされるが、別のパターンのバラバラにはならない。

 

次に「Ruby ランダムに並びかえる」で検索

https://docs.ruby-lang.org/ja/latest/method/Array/i/shuffle.html

shuffleメソッドというのがあるみたい。

とりあえず、

https://paiza.io/projects/hWaljlLyIJ5bfD4K63YkvA

を使ってちょっとどうなるのかやってみる。

a = [ 1,2,3]
puts a.shuffle

とすると、確かに並び変わっているみたい。

次に

a = [ first: 1 , second: 2 ,third: 3]
puts a.shuffle

としてみたら、並び変わっていないみたい。

 

そこでもう1つ書いてあるほうを使ってみる

a = [ 1, 2, 3 ]
rng = Random.new
a.shuffle(random: rng)

puts a

並び変わっていない。

 

shuffleメソッドについて検索してみる

https://qiita.com/maru53/items/39158b599c3a0ae59103

https://www.sejuku.net/blog/72965

shuffleでできそうなんだけど・・・

カリキュラムで検索を教えるものがあったので

新規作成し直し、そのファイルでshuffleメソッドを使用してみることに

<% @results = @results.shuffle %>

こんな感じでやったらしっかりランダム出力ができている。

ページ更新するたびに検索結果が変わっているのでできていると思う。

 

これでランダム出力はできそう。

 

次は出力時にランダムで表示するか、id順にするかを選択できるようにする。

if文で先程のshuffleメソッドがかかる記述とそうでない記述をそれぞれ部分テンプレートで呼べるようにすればいいか。

 

・検索画面でランダム表示か選べる場合

ランダム表示か、id順にするかは「モデルから問題を出力する条件」ではないから、現状のsearchに入れるのはどうするか

カラムを追加し、必ず3以上で登録させ、検索画面では1以上、2以上で選ばせる。そして、検索結果画面では1以上だったか、2以上だったかのif文で表示を変えるようにする。

→モデルの数だけ容量がデカくなっていくのは果たしてどうなのか

 

・検索結果画面では一覧で表示して、結果画面でランダムに並び替えられる場合

Javaでイベント発火すればいけるか

イベント発火→検索結果にshuffleメソッドを記入→

https://www.sejuku.net/blog/2531

reloadメソッドを使えばできそうか

 

どっちにしろ、検索結果画面で正規順、ランダムで変えられるようにはしたかったので、とりあえずこちらで実装するようにしてみる。

 

・検索結果画面に「ランダムで並び替える」を追加

Javaファイルを作成「ランダムで並び替える」にクリックでイベント発火するようにする

 

reloadメソッドを使用して、クリックで「ページを更新する」はできたが

<% @results = @results.shuffle %>

これをjavaで処理したい。

 

しばらく検索したが、特に使えそうなものにヒットしない。

現状の処理での問題点は、最初っからランダムで表示される。

当初の目的であった「検索するたびに並び順が変わる」は達成されている。しかし、

正しい順番に並びかえができない。

 

どちらにせよ、この@resultsをjava上で動かせないと正しい順番にもできない

 

しかし、元の構想通りに出力もしてみたいので、検索を続ける。

 

 

 

 

 

 

検索機能の実装

補足カリキュラムにあったransackを使って、検索ページを実装中。

補足カリキュラムだと今の使用に会っていなくてよくわからんから、

ransackを検索して、自分のしたいことに合うようなものを探してみることに

 

・_eqというヘルパーメソッドを使うと同じ名前のものを引っ張り出す。

・_contというヘルパーメソッドを使うとそれを持っているやつを引っ張り出す。

他にも今後使えそうなのがある。今回は使わないけど使えそう

Ransackのススメ - Qiita

 

いざ、実装するが、中間テーブル先の名前を引っ張り出す方法とかが怪しいので、中間テーブル先の呼び出しとかカリキュラムで確認してみるが、少し勝手が違うのかうまく行かない

とりあえず検索してみると

 

似たようなことをしているサイト様

[Rails]ransackを利用した色々な検索フォーム作成方法まとめ - Qiita

があったのでそれを参考に進行。

カリキュラムと同じような呼び出し方をしていたので、

カリキュラムであったことを補完しつつも進めることができた。

 

しかし、

 

collection.selectのところで相違がある。

カリキュラムにそって作った今の自分の設定だと

1つ目が--に設定されているから、検索するときにおかしくなってしまう。

 

例えば

カテゴリーの検索はかけなくていいと思ってカテゴリーを--にしておくと

今のままだとカテゴリーは1で検索をかけてしまう

カテゴリー1の--で登録しているitemなんてないから、検索に何もヒットしない。

(と予想している。)

 

要はid1だけ表示しないようにしたい。

・そういったid1だけ除く方法を探す

・モデルを指定しているところで、モデルの中身のような配列を打ち込む

が、解決策として考えられる。

 

allで取得しているから、それ以外にすればいい

すぐ出てきたのはfind

指定しているカテゴリーは確かにこちらが指定しているもので変更はない。

だからこれでもできる

⇨仕様変更の際に気が付きにくい

 

モデル 抜き出し とかで検索していたら

find,とかwhereがあって、whereはカリキュラムで使ったと思い確認。

 

【例】whereメソッド
1
モデル.where('検索対象となるカラムを含む条件式')

 

これかと思うんだけど。なんかエラーを吐く。

undefined method `keys' 

キー検索がうまくいっていないみたい

調べてみると

active_hashまとめ - Qiita

使用例にwhereがいない

元がハッシュなのがいけなさそうだが、、

 

ひとまず、進まないので、findで実装することに

activehashで実装している以上、変更はないデータなので、

いいちゃいいが、これからはそうはいかんはず。

 

ともかく表示はできたが、検索をできるようにしなければいけない。

早速、searchのviewファイルをいじって表示するように変えてみる。

先程の

[Rails]ransackを利用した色々な検索フォーム作成方法まとめ - Qiita

のファイルを参考に作ってみたが

SyntaxErrorが出る。

参考サイトはerbではなく、slim。それが原因かと思い、

erbとslimの違いを確認しにいく。

 

どうやら、

<% if flash.notice.present? %>

- flash.notice.present?

になるらしい。

 

表示はできたが、

表示が思ってたんと違う。

思ってたんのは

検索条件に引っかかる商品のデータを全て返す

今のは

検索条件に引っかかる文字を返す

になっている。

searchの条件式がおかしいので、今のファイルをカリキュラムと同じような条件分岐にできないか検討する

 

カリキュラムはresultに全部入れてresultから結果を出力している。

今のやつもitemsだけど同じように入れているように見える。

 

とりあえずエラーを吐かずに、searchにいけた

名前は問題ないように見えるが、

他が全く機能していない。

カテゴリーをメンズにしても、その他のものがかえってきている。

 

permitかけていたのを忘れていた。

ことごとくpermitを記述したところ、

うまくいった!

全部行くか一応試そうと思っていたら

タグ検索がうまくいっていない。

中間テーブルを解しているからか、、

 

ヘルパーメソッドをeqに変えたらうまくいった。

inの使い方がおかしかったのかな

とりあえず。これで検索機能もできた。

 

 

Formオブジェクトの編集、削除機能の実装

例によって即検索したところ

https://qiita.com/ogamw/items/f81c36428ae358e084c2

https://tomo-bb-aki0117115.hatenablog.com/entry/2020/10/31/005817

 

偉大な先人様に感謝しつつ、見てみると・・・

何やらとっても大変そう。

確かに、Formオブジェクトように作ったModelさんには、createのことしか書いてない。

だから、updateのときと区別しなければいけないという感じなのかな。

そもそも何がどうなっているのかをしっかり把握仕切れていない僕だと、

熟読だけで今日は終わりそうですね。

 

追記:

いや、編集はできたんだけど、今度は新規投稿ができなくなっている。

なんか全部updateになっちゃうんだよね。

先人様そのまま自分のに落とし込んだんだけど、

確かにどこでcreateしてんのかわからん。

次は、何しているか知らない初見のコードが何をしているのかを調べることにする。

 

追記2:

先人様は流石に偉大でした。

先人様のコードとの違いを目を皿にしてみていったら、修正してみた。

item = Item.update(....)していたのが原因。

item.update!(....)にしたらなおった。

 

中間テーブル先の値を表示したい

現状:

出品された商品にタグを表示したいが、

中間テーブル先のタグの名前を表示したい。

 

仮説検証:

①先程の反省を踏まえ、即検索

qiita.com

そのまんまのものが出てきたので、pluckメソッドとやらを使用してみる。

⇨反映されたが["野菜"]と出る。この[""]のところ、なんとかならないかな。

⇨検索してもすぐわからんし、野菜だけ抽出するような記述もやったことないからとり会えず保留とする。

他の実装が出来次第考えよう。

DBに保存ができない

現状:

商品にタグをつける機能を実装中。

商品の情報を保存するitemと商品のタグを保存するtagで保存する先が違うのでActiveModelを使って記述。

 

目的:

ActiveModelの復習がしたい。

 

発生した問題:

タグだけ保存される。エラーもでない。

 

現状からの仮説:

①rootに戻っているということはsaveは実行している。

⇨itemの中身がからになって保存されているのでは

  binding_pryでroot_pathにいく直前の@itemを確認したが中身がある。

 

②以前保存できないとき、コントローラがおかしかった。

⇨@item.valid?がないことが判明。

  次はTag Name is not numberがでたので修正したが、相変わらず保存ができない。

 

③僕には解けない

https://teratail.com/questions/121213 でuser_idが保存されないでできなかった人がいたので同じように修正。

 ⇨できた

 

結論:

はよ誰かに聞きましょう。

(保存されるカラム名は全て記述するようにする)