Web Marina

日々の業務や勉強などで得た知識をアウトプットしていきます。

【Rails】select_tagを使って表示内容を変える

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

今回はselect_tagを使ってページの表示内容を変える方法を書こうと思います。



概要

select_tagで項目を選択し、項目に合わせてページの内容を変える

この時、

  1. HTTPリクエストはGET
  2. 「項目.id」を使って関連付けされた「内容」を持ってくる
  3. submitボタンなしで切り替えを実現

こんな感じです。

なお、今回はクラスを選択すると生徒一覧が出てくる感じにしようと思います。




View

まずはviewsから。

<%= form_tag classes_path, method: :get do %>
    <%= select_tag 'classes', options_for_select(class_choices, @class_id), id: 'classes' %>
<% end %>

<% @students.each do |student| %>
    <%= student.name %>
<% end %>

<script>
    this.class_names.select();
</script>

前3行がセレクトで、中3行が生徒一覧を表示する部分、後3行がJSの呼び出しです。

JSについては最後に記述します。




セレクト部分

<%= form_tag classes_path, method: :get do %>
      
<% end %>

コントローラにselect_tagの情報を送るためにform_tagで囲みます。

新規作成とかのページではないのでgetのアクションを使っています。

そのためmethod: :getを指定してください。




<%= select_tag 'class_id', options_for_select(class_choices, @class_id), id: 'classes' %>

第一引数のclass_idは、後ほどコントローラにこのセレクトタグの情報を渡す時に使う名前です。

第二引数には選択肢の文字列を指定しますが、

今回はoptions_for_selectを使っています。

このメソッドは、配列などを使って選択肢を生成してくれるメソッドです。




この引数には、helperにクラス名とidをとってくるメソッドを定義し、

それを使うことにします。

def class_choices
    ClassName.all.map{|class_name| [class_name.name, class_name.id]}
end




@class_idは、デフォルトでは選択後に表示名が最初に戻ってしまうので、

それを選んだ項目そそのまま表示させるためのものです。

こちらはコントローラに定義します。




id: 'classes'は、後ほどJavaScriptに書く、

項目を選択するだけでデータを送れるようにするための処理で使います。




生徒表示一覧部分

<% @students.each do |student| %>
    <%= student.name %>
<% end %>

対して説明はいらないと思いますが、

コントローラで@studentsを定義し、

それを元に生徒の名前を羅列しています。




Controllers

次はControllersです。

def classes
    # select_tagのデフォルト値に使用
    class_first = ClassName.find(1)
    # select_tagから現在選択されているクラスのid取得
    # 未選択時はデフォルトのclass_first
    @class_id = params[class_id]  || class_first
    # クラスに属する生徒を取得
 @students = Student.where(class_id: @class_id)
end




@class_idは未選択時にデフォルトで何を入れるか明示してあげないと、

「IDないんですけど?」と怒られてしまうのできちんと指定してあげましょう。

このidをセットするところで、select_tagに付けた名前が使われます。

「viewにあるclass_idって名前のとこの値をとってきてね。」

という感じです。




最後に、とってきたクラスのidを元に

関連付けられた生徒の名前を取得し、

これを使って表示をします。




JavaScripts

最後にプラスαで、サブミットボタンを付けなくても中身が切り替わるよう、

coffeeファイルを記述します。

これがないと、いちいちボタンを作って、それを押さないと切り替わりません。

class ClassNamesController
    select: ->
        $('#classes).on 'change' () ->
            $(@).parents('form').submit()
        return
this.class_names = new ClassNamesController

言わずと知れたCoffeeScriptです。




コンストラクタ呼び出しを使っています。(new~の部分)

これの呼び出しが、最初のviewsに記述した

<script>
    this.class_names.select();
<script>

です。こうやってメソッドを呼び出すことができるようになるんですね。

ここの部分はいまいち理解仕切れていないので、

参考にさせていただいた記事をご紹介するに留めておきます。

JS: new 呼び出しについて理解する - Qiita

クラス - JavaScript | MDN




関数の中身です。

$('#classes').on 'change' () ->
    $(@).parents('form').submit() 

id="classes"が変更されたら

これ(@=this)の親のformを送信する。」

という内容ですね。

JS系は完全に勉強不足なので、自滅する前にここでやめておきます。




まとめ

今までselect_tagformの一部としてしか使ったことがなかったですが、

このような使い方もよく見かけるのでとても良い勉強になりました。

ユーザービリティなども考えると、このJSのコードもいろいろなケースで

使えそうだなと思います。

余談ですが、今回この記事を書くにあたって自分でサンプルを作ってやってみましたが、

仕事と同じようにやってもやはりうまくいかずそれでまた一つ勉強できたりしたので、

手を動かすことの大切さを改めて実感しました。

【CSS】セレクタの指定方法まとめ

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

プラグインCSSのコードを読んでて、

今更ながらセレクタの指定方法の認識が曖昧だったことに気づいたので、

忘れないうちにまとめておくことにしました。

こちらを参考にさせていただきました。

子供セレクタ - CSSの基本書式 - スタイルシート入門




基本

ユニバーサルセレクタ

ページ内の全ての要素を指定するセレクタ

* { }



タイプセレクタ

ページ内の要素を指定するセレクタbodyulpなどのタグ。

要素名 { }



IDセレクタ

/* 全ての指定ID属性 */
#属性値 { }

/* 指定要素下のID属性 */
要素名#属性値 { }



Classセレクタ

/* 全ての指定class属性 */
.属性値 { }

/* 指定要素下のclass属性 */
要素名.属性値 { }



複数セレクタ

セレクタ1, セレクタ2, セレクタ3, ... { }





応用

子供セレクタ

親子関係の子要素を指定する場合。

親要素 > 子要素 { }



子孫セレクタ

子要素、孫要素などの子孫要素を指定する場合。

/* 半角スペース区切り */
親要素 子孫要素 { }



隣接(兄弟)セレクタ

隣接要素と対象要素が直接隣接している時に指定する場合。

両方とも同一親要素の子にあたり、

先の要素が「兄」、次が「弟」になるため、

隣接兄弟セレクタとも言う。

隣接要素 + 対象要素



属性セレクタ

属性や属性値を特定して要素を指定する場合のあれこれ。(idやclassも含まれる)

属性を指定する

/* 指定属性が設定されている要素 */
要素名[属性名] { }

p[title] { }



指定属性に特定の値が設定されている要素

要素名[属性名="値"] { }

p[id="main"] { }



属性の値に指定の値が設定されている要素

「class属性の値に"main"が含まれているdiv要素」のように指定したい時。

要素名[属性名~="値"] { }

div[class~="main"] { }  



属性値が一致する要素

「lang属性の値で"en"と一致、または"en-~"で始まるものと一致するもの」のように指定したい時。

要素名[属性名|= "値"] { }

/* 全体でlang属性の値が"en"または"en-~"のもの*/
*[lang|="en"] { } 



指定の属性に特定の値が全て設定されているもの

「class属性の値に"foo"と"bar"の両方が設定されているもの」のように指定したい時。

属性値属性値 { }

/* classにcontentsとmainが両方あるもの */
.contents.main { } 

【Rails】paramsがなんなのかやっとわかった!

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

Railsformとか使っていれば当然paramsって出てきますよね。

恥ずかしながら、このparamsの認識が違っていたために職場でかなり苦労しました。

やっと見えてきたのでまとめようと思います。

*内容に間違いや補足などございましたら、是非ともご指摘ください!



paramsとは?

paramsメソッドとは、URLから送られてくる情報を受け取るメソッドです。

初っ端ですが、私の認識はここが間違っていました。

DBのレコードが持つ情報やカラムに紐付いた情報を表していると思っていました。

なんでこんな誤解が生まれたかはちょっと謎ですが・・・

まぁ、教材を流し読みしたせいだと思います。


とにかく、paramはURLからの情報を受け取るメソッド。

このことがわかれば、これまで職場で???となっていた全ての現象の辻褄が合いました。

paramsで受け取れるもの

formの値

まずは言わずと知れたformから送られてくる値ですね。

<%= form_for @user do |f| %>

   <%= f.text_field :name %>

<% end %>
@user = User.find_by(name: params[:name])

form_forで入力された:nameを元にユーザーを探し出してます。

ここまでは良かったのですが、これがform_tagに成った時に一気に躓きました。

<%= form_tag hoge_hoge_path do %>

   <%= password_field_tag 'password_text', ' ' %>

<% end %>
@password = params[:password_text]

ただ入力されたパスワードを変数に入れてるだけなんですけど、

私はparamsで扱えるのがDBのカラムにあるものだけだと思っていたので、

ここで混乱するわけですね。

「password_textなんてカラムないけど!?」と。

なんともお恥ずかしい話です。

何はともあれ、URLからの値を受け取るので当然このように使用することが可能です。

クエリパラメータ

クエリパラメータとはURLに付け加えられたページの情報です。

http://hogehoge/hugahuga/hello?name=marin

この?以降がクエリパラメータです。

paramsではこの値も受け取ることができます。


この場合はlink_toからの値ということになると思います。

<%= link_to "marin", hogehoge_hugahuga_path(name: 'marin')
@user = User.find_by(name: params[:name])

こんな感じでしょうか。


番外 〜ストロングパラメータ〜

なんか悪い人からの攻撃に対応するために扱う値を制限するってやつですね。

チュートリアルとかでもやってるんですが、多分ここが私の誤解の原点ではないかと思います。

params.require(:user).permit(:id, :name, :email)

こんな感じのやつですね。

usersテーブルのid``name``emailは扱えますよと言っています。

(多分ここでテーブルに関連する値しかparamsは使えないと勘違いしたのではと)

大体の場合は

def hugahuga
   @user = params(user_params)
end


private

def user_params
    params.require(:user).permit(:id, :name, :email)
end

という感じで使うのではないでしょうか?

まとめ

そういえば、formではhidden_fieldなんてのに渡した値を取得する場面もありました。

自分のどこが理解できていなかったのか?何を間違えているのか?

それがわかれば本当はもっと簡単にコードが読めるのかもしれません。。。

わからない時には一度立ち止まって、

つまづいている部分がどこなのかを見極めるのも大事ですね。

【Rails】なんか変なとこのヘルパー読み込んでるんだけど??〜config.action_controller.include_all_helpers〜

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

昨日こんなことがありました。

「staffのネームスペースでuserのhelper読み込んでるんです・・・」

その時に教えていただいた対処法をご紹介します。



概要

  1. app/helpers/user/pages_helper.rbにhogeと言うメソッドがあった。

  2. 同じ処理を行いたいのでapp/helpers/staff/pages_helper.rbにもhogeと言うメソッドを書いた。

  3. app/controllers/staff/staffs_controller.rbからネームスペースstaffのhogeを呼び出した。(つもり)

  4. なぜかネームスペースuserのhogeが呼ばれてしまう!?

こういった経緯で自己解決できずに相談しました。


原因

Railsのcontrollerでは、デフォルトで全部のhelperを読み込むようになっているそうです。

なので意図しないファイルを読み込んでいる場合は、

キチンとネームスペース内のものだけを読み込むように設定しないといけないとのこと。


対処方法

config/application.rbファイルに以下を記述します。

config.action_controller.include_all_helpers = false

これはデフォルトで全てのヘルパーを読み込むようになっているのを、

「やめて〜〜!!」

と言っています。

これでネームスペースに則ったヘルパーのみを読み込んでくれるようになるということです。


基礎を勉強するだけではわからないこと、

気付けなかったこと、ぶち当たらなかった壁をたくさん経験できて、

やはり一歩踏み出して良かったな。

と感じる今日この頃です。

【Rails】remote: trueでサクッと画面切り替え

f:id:song-of-life1352607:20170220105406j:plain

こんにちは。マリンです。

数ヶ月ぶりの更新となってしました。今まで何をしていたかと言いますと・・・

アルバイト先で修行しておりました。否。「おります」進行形です。

でもちょっとだけ余裕ができたので、これまでに学んだことを少しずつここで残せたらと思います。

まず最初は、「空気のごとく使えるようになること!」と言われたremote: trueについてです。

*内容に間違いや補足などございましたら、是非ともご指摘ください!



remote: trueとは

そもそもremote: trueとは?

それはAjaxを使って画面を切り替えることができる機能です。

Ajaxについてはこちらの記事でわかりやすく説明して下さっています。

初心者目線でAjaxの説明 - Qiita )

つまり、通常ではまるっとページ全部を読み込んで画面遷移するところを、

その部分だけ差し替えて画面を切り替えてしまおうってことですね。


remote: trueの使い方

では本題の使い方です。

バイト先でよく使った登録フォームを例にとって説明します。

「新規登録」のリンクをクリックすると、登録フォームがremote: trueで表示されるようにします。


index.html.erb

Ajax切り替え用のremoteオプションと、

中身を差し替える部分にidを用意します。

remoteオプションは色んなところで使えますが、今回はlink_toにつけます。

app/views/users/index.html.erb

<%= link_to "新規登録", user_new_path, remote: true %>
<div id="samplRemote"></div>



Ajaxへの切り替え処理はこれだけです。

new.js.erb

お恥ずかしいことにコントローラが探しに行くテンプレート

(というかviewsに置けるファイル)って、

〜html.erbだけだと思っていました。

でも本当はjsとかもあるんですね・・・

remote: trueの場合はまずこちらを探してもらって、

このjsファイルの中でrenderを使って_new.html.erbを呼び出します。


app/views/users/new.js.erb

$('#samplRemote').html("<%= escape_javascript(render 'new') %>");

対象のidはindexの差し替える部分につけたid=samplRemoteです。

.html_new.html.erbを読み込むよう、

要素を書き換えています。


_new.html.erb

ページの中身はパーシャルとして_new.html.erbの中に書きます。


app/views/users/_new.html.erb

<%= form_for @user do |f| %>

# フォームの中身

<% end %>



これでAjaxを使った画面の差し替えが完了です。

コントローラは通常通りのnewcreateで大丈夫です。


remote: trueが使えるもの

最後に、このremoteオプションが使えるものです。

  • form_for

  • form_tag

  • link_to

  • button_to

他にもあったら是非教えてください。


まとめ

まだまだちゃんと学ぶと奥が深そうなremote: trueですが、

今はとりあえずこんな感じで使えるようになりました。

ロード時間の短縮にもなるし、サーバーへの負担も軽減されるし、

大変ありがたい機能ですね!

【rails】ページごとにCSSを当てたい。

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

知人に頼まれてHTML、CSS、JSのみの完全静的Webサイトを作ったのですが、

このたび機能追加に伴いRails化することにしました。

それまでのコードはペペッと貼り付けてちょこちょこっと修正すればいっかなぁ〜

なんて軽く考えておりましたが、そこはさすがプログラミング。

そう甘くはないですよねぇ・・・


まだまだ色々試行錯誤中ですが、ひとまず今回解決しました、

CSS周りの適用の仕方を書きます。



私はcustom.scssしか知りません

Rails Tutoialでは、それぞれのスタイルを順番に読み込むのは大変!

ということでcustom.scssのみでスタイリングしておりました。

しかし今回、ページごとにスタイルをつけてしまったんですよね。


ダブってるスタイルを別々にし直すのも手ですが、

できればそんな面倒なことはしたくありません。

そこでこちらでもやはり分けた部分はそのまま分けることにしました。


ここで問題になるのは、

これまでごっそりスタイル当ててたcustom.scssしか知らないということです。

個別にスタイルを当てる方法を知らなかったんですね。


問題1:適用の仕方

もちろん最初そのままのコードを使っていましたから、

Viewでの指定の仕方が

<link rel="stylesheet" href="stylesheets/home.css">

なんてことになっていました。


当然うまくいかないわけで、

そこで思い出したのがapplication.cssにあるアレでした。

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>

あ。これだ!と思ってapplidationの部分をhomeに変更してみました。


問題2:precompile

assets.rb

上記だけで実行しようとすると次のようなエラーが出ます。

ActionView::Template::Error (Asset was not declared to be precompiled in production.
Add `Rails.application.config.assets.precompile += %w( home.css )` to `config/initializers/assets.rb` and rest
art your server):

注意された通りなんですが、

config/initializers/assets.rb

にある

Rails.application.config.assets.precompile += %w( home.css )

この一文のコメントアウトを外して、

( )の中に対象となるCSSなどのファイル名を入れます。


precompile

これでもまだ不十分です。

再読み込みしても同じエラーが出ると思います。

更新を反映させるためにはプリコンパイルが必要になります。

ターミナルで以下の処理を行ってください。

$ rails assets:precompile 
# もしくはrake assets:precompile




最後にサーバーを再起動させれば完了です。

*これをお忘れなきよう。私は数分困惑しました・・・


まとめ

ここまでやってきた処理は、

「Asset Piplineのコンパイル対象を追加する」という作業になるそうです。

へぇ〜と流してきたAsset Piplineですが、いい機会なので勉強してみてもいいかもです。

とはいえ、これやるとページの読み込みが遅くなるようなので、

やはりスタイルなんかは一つのファイルにまとめた方が良さそうです。

今回はこちらを参考にさせていただきました。

RailsでAsset Pipelineのコンパイル対象を追加する by 42 Design Work

RailsでCSS(スタイルシート)を読み込みたい | Ruby on Railsサービス開発逆引き辞典

RSpecを学んでいて気付いた自分の弱点。

f:id:song-of-life1352607:20170220105406j:plain

こんにちは、マリンです。

この度アルバイト先が決まりまして、その会社で「RSpec使ってるから勉強しといて」と言われて今学んでおります。

今回の記事はただのぼやきです。すみません。

今回のテーマ

タイトルの通りです。

RSpecを勉強していて自分の決定的に足りない部分を見つけました。


RSpecについてはいろいろとサイトを巡って情報を集めており、

基本的な書き方は覚えました。

なので以前勉強したRails Tutorialを、今度はRSpecを使ってやってみようと思い立ったのですね。

だがしかし、全っ然出来ないんですよ!


テストが苦手なことは気づいていました。

正直、自分で楽しくやっていた時はスルーしてました。

でもまさかヒントがあるのに書けないとは・・・

そこで自分の最大の弱点に気づいたんです。

私の弱点

ズバリ、「応用力」です。

これが決定的に欠けています。

いや、数学がめっきりダメだった中学の頃から知ってはいたんですけど・・・


なんでテストが書けないかって考えてみると答えは簡単。

アプリ自体は結構そのまま使えたり、ほんのちょっと変えれば使えるようなコードが結構出てくるんですよね。

でもそれについてのテストコードまでは載ってないんです。

だから検索して出てきたコードを参考に自分のサイト作ったりはできますが、

それに対するテストは自分で考えなきゃだからできないんですね。


チュートリアルで基本は学びましたが、

そこから発展していざ自分で考えるとなると・・・

「え?どうすればいいんだっけ??」

となってしまうわけです。

で、どうすんの?

とはいえこのままでは使い物になりません。

そこで、なんで基本は学んだのに自分で考えて書けないのか?を考えてみました。

もちろん応用力が足りないからなんですけど・・・

その応用力に必要なものはなんだ?ともう一歩踏み込んで。


多分、「問題の本質を理解しているか?」だと思います。

目の前にある「〇〇をテストする」ということばかり考えて、

「え?どうやるの??」ってなっているから進まないのだと思うんですよね。

必要なのは、

「〇〇をテストする」

  1. 〇〇はどうやって動いているのか?
  2. どの部分をテストすれば良いのか?
  3. そのテストには何が必要なのか?

といった、〇〇という対象に対してもっと噛み砕いて構造や振る舞い、必要なものを考えないから、

何をしていいのか?がわからないんだと思いました。

前に勧められて読んだ本に書いてありましたが、

問題を解決するには、いかに噛み砕いて問題の本質を理解するかが鍵だそうです。


とはいえ・・・これ今から鍛えて間に合うのか???

と焦っています。

そしてそもそも圧倒的に足りない情報量・・・

どうしよう。

情報収集もしたいし、でもまだ基礎も完璧ではないし、

そして応用効かないし・・・

中学高校と「数学ムリ〜ww」とかヘラヘラしていた自分を呪います。