Play Framework 2.0 Session?いえいえ Cookie でしょ

Play Framework 2.0 で、ページを戻った時に、以前の選択した内容を再現したいなーというお話。
となると、値はどこかに保存しておかなきゃいけないわけで。
Session? いえいえ、ステートレスな Play ! だからこそ、Session の概念はポイしなきゃ。
値の保存は Cookie に頼りましょ。

Java であれこれ作ってきて、サーブレット + jsp に慣れ親しんだ身としては、
情報の保持を考えるときに、ついつい Session に頼りがちです。
でも、ステートレスを特徴とする Play Framework には Session は似合いません。
ステートレスってなあに?という人にはこことかが分かりやすいです。
クラウドベース、複数サーバー、非同期通信で随時接続、サーバーからの情報の頻繁なプッシュ‥‥
これらを考慮したうえで軽量なサーバー環境を実現するのはステートレスでないと割が合わないのです。
(参照:こことか

それでも、「単に軽量なフレームワークとして Play! を選んだだけ!」
「だから Session を使いたい!」という方もいるはず。

Play! にも Session と呼ばれるものは存在します。
(参照:Play! Japan Doc. 2.0.4 セッションとフラッシュスコープ
ただし、このセッション機能は以下のような注意点があります。
「Cookie を利用してる」「サーバーにデータは残らない」
「上限は 4KB」「格納できるのは String のみ」
ここで Session と呼ばれているものは、実際には Cookie だということ。

「これじゃダメ! Servlet の HttpSession がどうしても使いたいんだ!」という方には、
プラグインやライブラリを導入することをお勧めしておきます。
(参照:Play2 で HttpSession を使う

せっかくなのでここからは、ビュー側で jQuery を使って cookie を扱う方法をまとめときましょう。
jQuery で cookie を扱う時には jQuery のプラグイン jquery-cookie を使うととっても便利。

github などからダウンロードしたら、ビューのあたまでインポートしておくことを忘れずに。

cookie への値のセットと、セットした値の参照、値のクリアはこんな感じ。

セットした値をクリアしたいときは null をセットします。
こうしておけば、ブラウザを閉じるまでは null 値が cookie に残っちゃいますが、
ブラウザを閉じると、キーのセットごとクリアしてくれます。

また、cookie の対象とするパスや有効期限( expires )を手書きで設定することもできます。
こんな感じ。

こんな風に、第3引数に配列で指定します。
特定の時分秒に expires させたいときなども、Date 関数で日付を作ってしまえば簡単。

ちなみに、第3引数が設定されていない場合は、
path はそのスクリプトが動作したパスが、expires は -1 が、それぞれセットされます。

最後は、ビューで受け取った値を cookie にセットする方法と、
値の存在チェックをして、実際に値を使う時の例をまとめておきましょう。
Play! の機能としての Session は特に使わず、ベタベタな方法で cookie を利用しています。

値をセットするビューの頭で、Scala のメソッドを定義します。
ビューで受け取った値を cookie にそのまま放り込んじゃう感じ。

先ほど Scala で定義したメソッドを javascript 側で使います。
わざわざ Scala のメソッドを使う意味はあまりないかもですけど、
条件分岐なんかがある場合はこの方法が便利なはず。

そして、cookie に保存した値を別のビューの javascript で、
値の存在をチェックしたうえで使います。

こんな感じ。
ここでは、連結された cookie を分割して配列に保存しています。

好みの問題になりますが、cookie に複数の値を保存するときは、
たくさんのキーに散らばった状態で保存するよりも、
ここで上げたように、文字列を連結して保存したほうが、管理が楽な気がします。
もちろん、連結文字を扱わないように気を付けてあげる必要が出てきちゃいますが‥‥。

というわけで、今日は Play! で Session と cookie について考えたというお話でした。

Play Framework 2.0 ‥‥絶望的?複数チェックボックスの値を送信する

最初に。余裕のある人は、Play Framework 2.1 を使うことをおすすめしますよ~‥‥

ということで、Java + Play! 2.0 で、複数チェックボックスの値を送信する方法です。
調べた限りでは、どうやら Java + Play! 2.0 の環境では、
multiple checkbox の binding 処理にバグがあるみたい‥‥。
(参考:こことか。)
もちろん、select ボックスの複数選択も同じくできないようですね。

で、この内容、Play! 2.1 では修正されているみたいなんです。(参考:こことか。)
なので、それでもあえて Java + Play! 2.0 で複数チェックボックスを扱わなきゃいけない!
という人以外には、下記の方法はおすすめしないかもしれません。

さて、では本題です。
バグがある。正攻法じゃダメ。じゃあどうするか‥‥ javascript に頼るしかないですねえ。
値を区切り文字を挟んで連結して、hidden 項目に入れて送信しちゃいましょう。
やや面倒ですが、ほかに思いつく楽な方法がありませんから‥‥しょうがないです。

手順はこんな感じ。
1. フォームの submit イベントを拾う。
2. チェックボックスの値を区切り文字を挟みながら連結する。
3. 連結した値は hidden 項目に格納。
4. フォームを送信。
5. 受け取った内容を区切り文字で分割。

ソースを見ていきましょう。
まず html から

チェックボックスは submit 時に値を付ける必要がありませんので、name を宣言していません。

javascirpt はこんな感じです。

今回はカンマ区切りで連結してみました。
hidden フォームに格納してしまえば、あとは普通のフォームの値と同様に扱えます。

半分以上がおまけの機能=全てチェックするためのボタンのための処理です。
「全てチェック」は、よくチェックボックスで機能を用意してある場合がありますが、
動作の整合性を取るための機能の実装に混乱をきたしがちです。
だったらいっそのことボタンにしちゃったほうが楽でしょ?

次にコントローラー側では、受け取ったフォームの値を区切り文字で分割してあげないといけません。

受け取った値は、ここには書いていない TestFormData クラスで受け取ることとします。
また、DB から取得した内容は、これまたここには書いていない PREF_TBL クラスで受け取ることとします。
java.util.regex.Pattern.split で分割した値を java.util.Arrays.asList で配列に保存しています。
あとは org.apache.commons.lang3.StringUtils.join で
区切り文字を入れながら再度連結なんてこともしています。

routes については書いてませんが、一般的な Form の POST 処理で OK ですので、
悩ましいところはないはずです。

いかがでしたでしょうか。
複数チェックボックスの値を扱えない‥‥というところがコマッタさんでしたが、
javascript を使えば難なくクリアできちゃいますよというお話でした。

ie8やie9で動的に生成した項目の表示スタイルが乱れる

CSSにバグはない、javascriptも問題ない、なのになぜ?ieだから?

jQueryを使ってselectボックスの中身を動的に生成するようなケースで、ie8やie9で見てみると、
selectボックスの横幅がぐーんと伸びたのに、selectボックスを含むエレメントの横幅が伸びなくって、
ボックスが横へはみ出してしまうように見えちゃったというお話です。

文字だけだと説明がまどろっこしいですね‥‥。

今回はこのあたりを参考にボックス追加の処理を書いてます~。
引用すると、こんな感じ。

ええ、いまだにie6対応求められてるんですっ!
オキャクサマ都合とはいえ、システム屋さんの悲しい現実ですよねえ。

‥‥それはさておき
この時にieの「ズーム」機能を使ってズームを変更すると、
変更した時だけ正常に描画される!という現象が見られます。
でもその状態でまた動的に表示を変更すると、ズレが再発‥‥。

他のブラウザだと正常に表示されるので、CSSやjavascriptの問題ではないはず。
でもie8とie9だけはselectボックスとほかの要素が重なったり表示が乱れたり、
とにかくちゃんと画面が表示されません。ieのバグなわけ?

実はこの現象、ieの「互換表示設定」が原因です。
未確認ですが、ie10でも発生するかもしれません。
解消するには、「ツール」>「互換表示設定」で互換表示設定パネルを開きましょう。

ie9_gokan

ie9_gokan2
ここの「互換表示ですべてのWebサイトを表示する」をオフにします。

‥‥え、そこが最初からオフになってる場合はどうするんだって?
そうですね‥‥「互換表示ですべてのWebサイトを表示する」は、意図しない限りデフォルトではオフです。

では、今見ているページはローカルネットワークに作成したサイトではありませんか?
つまり「localhost」や「192.168.1….」なんて、IPアドレス直打ちでページを表示していませんか?
ポイントはイントラネット向けの設定がインターネット向けの設定とは別になっているところ。
「互換表示でイントラネットサイトを表示する」もオフにしましょう。

これで解決しました?

互換表示については普段意識しないだけでなく、
ローカルネットワークのページを表示している場合は、互換表示ボタンは表示されないんです。
そんなわけで、意外と気づきにくい落とし穴なのでしたー。