Web Marina

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

【jQuery】クリックでテーブルの行をハイライトさせる。

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

皆さん明けまして(だいぶ経ちますが)おめでとうございます。

本年もまったり更新していこうと思いますので、どうぞよろしくお願い致します。


さて、2019年最初の一発目は、

jQuryです!

最近「勉強してください・・・」と上司にガッカリされましたorz

今回は、クリックしたテーブルの行をハイライトさせてみました。

ポイントはハイライトなので”1行だけ”というところです。

クリックした行全ての色が変わるのを防止します。




ソースコード

今回のコードはCoffeeScriptで書いています。

(基本Railsで書いてるので...)

highlightRow: ->
    $('.select-row').off 'click'
    $('.select-row').on 'click', () ->
        currentTr = $(@).parents('tr')
        if currentTr.hasClass('selected')
            currentTr.removeClass('selected')
        else
            $('tbody>tr').removeClass('selected')
            currentTr.addClass('selected')

select-rowというクラス名のボタンをクリックすると、

その行をハイライトし、もう一度クリックすると消えます。


// ハイライト用のスタイル
.selected {
 background-color: #f5f5f5;
}

ハイライトをする行にはclass="selected"を付け、

スタイルシートselectedにハイライト用のスタイルを適用させます。

ハイライトのON/OFFはこのselectedの有無で切り替えます。


解説

ハイライトOFF

まずは最初からハイライトを消すところまでです。

// 読み込まれた際に一度イベントハンドラーを削除。
$('.select-row').off 'click'

$('.select-row').on 'click', () ->
    // クリックされたselect-rowの親のtrを持ってくる。
    currentTr = $(@).parents('tr')
    // 親のtrが既にselectedを持っていたら(ハイライト状態なら)
    if currentTr.hasClass('selected')
        // selectedを削除してハイライトを解除する。
        currentTr.removeClass('selected')
    else
        // ハイライト用

今回はボタンにselect-rowをつけ、それをクリックすることでハイライトを切り替えます。

なのでスタイルを行全体につけるため、

クリックした時にまずはボタンの親要素であるtrを取得します。


次に、取得したtrが既にselectedを持っていたら、

そのクラスを削除します。

これでハイライトを解除します。


ハイライトON

最後にハイライトをONにする部分です。

else以降を書きます。

 .
 .
 .
 else
  // 一度全trのselectedを削除する。
  $('tbody > tr').removeClass('selected')
  // currentTrにだけselectedを追加。(ハイライト)
    currentTr.addClass('selected')

currentTrへのselected追加だけだと、

どこかをハイライトした状態で別の行のボタンをクリックすると、

そちらの行と前にクリックした行の両方がハイライトしてしまいます。

そのため、一度全部のtrからselectedを削除します。


改めて対象の行にselectedを追加。

これでその行だけをハイライトすることができます。


まとめ

今まで人の書いたものを真似するだけでしたが、

今回が初めて自分で考えて作る作業となりました。

最初は「全部のtrからクラスを削除する」ことをしなかったために、

クリックした行全てがハイライトされてしまったり、

$('tbody > tr')セレクターの取り方をせず、

tbody配下のtrを一つずすeachで回してselectedを調べて...」

なんてjQueryの意味がない!なんてことやってました。

結局上司の助言をいただいて完成した形になりましたが、

良い勉強になりました。

今年はjQueryもいっぱい勉強することになりそうです。

皆さま、本年もどうぞご教授のほど、宜しくお願い致します。

【Git】コマンド: いい加減覚えが悪いのでまとめとく。

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

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

7月から今の職場でお世話になり始め、はや5ヶ月経ちます。

・・・が、いつも使ってるGitコマンドを全然覚えなくていい加減上司にブチ切れられそうなので、

理解も兼ねてまとめておきます・・・orz




git fetch

ローカルの環境を最新の状態にします。

と言ってもこの時点では作業中のファイルが更新されるわけではありません。

更新されるのは、ローカルにあるorigin/masterです。

リモートにあるmasterを参照して、ローカルのorigin/masterを更新します。




originとmaster

二つは別物で、両方ともローカルにあるブランチです。

(masterはリモートにもあるブランチですが、リアルタイムで連携してるわけではないです)

origin/master

  • リモートの状況を追跡するためのブランチ

  • originという名前のリモートの、masterブランチを追跡していますよ。

  • git fetchすることでこのブランチが最新になる。(連携は手動で。)




master

  • ただの作業ブランチ(語弊がある??)

  • issue~create-userとか、そんな感じと一緒。

  • git fetchしただけでは変更されない。




originとmasterについてはこちらの記事がオススメです。

gitのふわっとした知識を調査してみたnekosoftware.wordpress.com


git checkout ブランチ名

対象のブランチに切り替えます。

もしそのブランチがローカルにない場合には、新規に作成して切り替えます。

そして新しいブランチで作業を始める際、

いつもこれとfetchを組み合わせて使っています。

$git fetch && git checkout ブランチ名




git tr --all

正確には

$git tree --all

です。

最新までのgitのツリー状況を見ることができます。


git merge origin/ブランチ名

先にgit fetchを出しましたが、あれだけでは作業ディレクトリは最新になりません。

mergeをすることで、初めて現ブランチを最新にできます。

現在いるブランチを指定したブランチの状態に上書きします。

(上書きという表現が正しいか分かりませんが、私は一番しっくりきました。)


ブランチを最新にする一連の流れ

$git fetch
$git tr --all
$git merge origin/ブランチ名
  1. fetchでローカルのツリーを最新に
  2. trでマージするブランチを確認
  3. mergeで現在のブランチを最新にする




git pull

職場では使っていませんが、

前述のブランチを最新にする一連の流れをまとめたコマンドです。

手間は省けますが、やはり一つずつ確認してからのが安心な気がします。


git diff

差分を確認するコマンドです。

確認対象は様々に指定できますが、指定がない場合は

インデックス(add後commit前のもの)と作業ディレクトリとの差分を表示します。

それぞれの指定方法はこちらが分かりやすかったです。

qiita.com




まとめ

今回はこんな感じでしょうか。

いつも迷うものをあげたので、commitとかpushとかは出していません。

多分コマンド自体の意味より、

originとかmasterとかがよくわかっていなかったから、

これをするとどこに何が反映される?という点でこんがらがっていたんだと思います。

まだ怪しい気がするので、もし間違いがありましたらぜひご指摘ください。

よろしくお願い致します。

【Rails】$rake db:rollbackしたい時の間違えない手順

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

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

未だ初心者の域を脱しない私は、よくmigration周りでやらかします。

一人でやっていた頃は失敗すると

ロールバックしちゃえ〜」「migrationファイル消しちゃえ〜」

とかやっていましたが、

チームでやっているとそんな横暴は許されません・・・

今回丁度勉強で作っていたサンプルで失敗したので、

ロールバックの正しいやり方?を学びました。




migration周りの注意

そもそもなぜこのあたり注意しなきゃいけないのか?

今更気づいたので一応書いておきます。


migrationファイルを書き換えたり削除した場合、

当然次回$rake db:migrateした時にテーブル構造が変わってきます。

一人でやっている分には大して支障はないですが、

これをチームでやっているリモートリポジトリなんかにあげちゃったりすると・・・


それをマージした他の方の環境まで変わってしまい、

最悪「コードの辻褄が合わない!」なんてことになりかねません。

ということで、この辺のファイルがどれほど恐ろしいものか気づいた私は、

今後は普段から取り扱いに注意する癖をつけようと肝に銘じました。


rollbackの手順

さて本題です。前述の通りこの辺は取り扱い注意なので、

失敗しない手順を学びました。


現在の状況を確認する

まずはどこまでmigrateが実行されているかを確認しましょう。

$rake db:version

このコマンドで確認できます。


$ rails db:version
Current version: 20171112122350

こんな感じで出てきます。

20171112122350まで実行されていますね。


ロールバック

次に戻したいところまでロールバックします。

[一つ前まで戻す]

$rake db:rollback



[任意のファイルまで戻す]

$rake db:rollback STEP=○

現在地を含めた○個前までなかったことにしてくれます。

(つまり○+1個前のファイルまで実行された状態)


migrateされていないファイルを確認

念のためどのファイルが実行されていないか確認します。

$rake db:abort_if_pending_migrations
You have 1 pending migrations:
  20171112131249 CreateUsers

20171112131249のUserテーブルが作られていないようです。


ファイルを修正

class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.references :comment, index: true, foreign_key: true    # null: falseを削除
      t.string :name
      t.string :email
      t.timestamps
    end
  end
end

今回はCommentテーブルにレコードが何も入っていない状態で

NOT NULLにしてしまったため失敗したので、

null: falseを削除します。


migrate

修正が完了したらマイグレートを実行します。

$rake db:migrate
== 20171112131249 CreateUsers: migrating ===============================
-- create_table(:users)
   -> 0.0026s
== 20171112131249 CreateUsers: migrated (0.0027s) ========================

これで無事修正完了です。


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

www.d-wood.com

追記

1ヶ所$rails db:versionと書いてしまいましたが、

Rails5の場合これでもいけます。

Rails4以前の場合は$rakeでお願いします。

【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

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

「やめて〜〜!!」

と言っています。

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


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

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

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

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