この2番目のメニューにはすべての国の州(state, county)が含まれるためにとても長くなり、選択しにくくなります。そこで、ユーザが1つ目のメニューで国を選択したときに、2つ目のメニューにフィルターがかかってその国に関連する項目だけが表示されれば、このフォームはとても使いやすくなるでしょう。
2つのドロップダウンメニューにはcollection_selectを使ってデータが表示されますが、 これはフォームでbelongs_toの関連のデータを表示する場合の通常のやり方です。今回のアプリケーションでは、PersonはCountry(国)とState(州)の両方に属して(belongs_to)います。
これはよりよいユーザ体験を提供するだけでなく、ドロップダウンリストでどの州がどの国に属するかをJavaScriptが知るための情報があるということを意味します。
次にフィルター用のコードを書き始めますが、これはCoffeeScriptで書きます。
このスクリプトではまずDOMがロードされたことを確認します。DOMがロードされたら、州のドロップダウンをid(今回はperson_state_id)から取得し、HTML(すべての選択肢を含む)を変数にコピーします。これは、ドロップダウンが変更されるときにすべての内容のコピーを保持している必要があるからです。
ユーザが国のドロップダウンを変更したら、表示される州も変更しなければいけないので、変更のイベント発生時に実行される関数を追加します。この関数ではjQueryの:selectedセレクタを使って選択された項目のテキストを取得してcountry変数に保持します。
次に州にフィルターをかけて、選択された国の州だけを表示させます。ここでfilterを使って選択された国の州を取得します。states変数はHTML文字列として国と州を保持します(その一部を下に示しています)。 そこでラベルによって正しいoptgroupを見つけて内容を取得することで、希望通りにリストにフィルターをかけることができます。
選択肢が見つかったら州のドロップダウンに値を埋め、見つからなかったらそれを空にします。
正しく動作するかを見るためにフォームを試してみます。1つ目のドロップダウンで国、例えばオーストラリア、を選択すると、2つ目にはオーストラリアの州だけが表示されるようになりました。州がない国を選択すると、2つ目のドロップダウンは空になります。
ユーザが州を持たない国を選択した場合には、2つ目のドロップダウンがまったく表示されなくした方がいいでしょう。これを組み込むのは簡単です。
2つ目のドロップダウンはデフォルトでは隠されていて、ユーザが州がある国を選択した場合のみ表示されるようになりました。
国名に特殊文字、特にシングルクォート(')が含まれる場合(例えばCôte d'Ivoire)、直接jQueryセレクタに名前を埋めこんでいるため、JavaScriptコードで問題が発生します。このような名前はエスケープ処理した方がいいので、CoffeeScriptコードを修正します。
動的なメニューはどの国名を選択しても正しく機能するようになりました。
今回のエピソードは以上です。今回書いたコードを、オリジナルのエピソード88のJavaScriptを動的に生成するコードと比べてみると面白いでしょう。このソリューションはより控えめ(less obtrusive)で、ブラウザのJavaScriptが無効化されていても正しく動作します。