車輪の二度漬け

railsを触っていましたが、最近はreactに興味あります。

最近の学び

仕事で久々にrailsでview周りを触ったので、そのことについて書いておく。

selectでdata-id

ユーザーへの情報の一覧を表示する管理者向けの画面に、その情報がユーザーへ公開されているか否かを表示する項目があった。
これまでは別途情報ごとの編集画面に行きそのフラグを更新していたが、管理者側の要望で情報の一覧画面からそのフラグ更新をしたいと要望があったので、その改修を行った。

f:id:wheeltwice:20190520140719p:plain
現行

f:id:wheeltwice:20190520141146p:plain
改修後:セレクトボックスを変更したらajaxでフラグを更新

はじめは

<% @information.each do |info| %>

中略

 <select name="published[#{info.id}]">
  <%= options_for_select({ '公開': true, '非公開': false }) %>
</select>

というふうに書いていたんだけど、レビュー時にname属性にidを入れるよりもdata-*属性にidを入れるほうが適切と言われた。

developer.mozilla.org

MDNの説明的にも確かにそうだなあと思い以下のように変更:

 <select name="published" data-id="#{info.id}">
  <%= options_for_select({ '公開': true, '非公開': false }) %>
</select>

ajaxでの送信の際、nameにidを混ぜているとsliceなどを使った文字列操作が入りめんどくさかったが data-*属性を使うとidの取得が簡単になるので便利。

ただ、ひとつ注意すべきなのは、railsselectヘルパー(formの内部で使うやつではないほう)ではdata-*属性が使えなかった(下のようなやり方はできなかった)。
selectヘルパーとform.selectヘルパーで違うのか、railsのバージョンが新しいからなのかはよくわからない。

stackoverflow.com

bubbling phaseでイベント取得

上のセレクトボックスの話に関してもうひとつ続ける。

上のような、行う処理は同じで渡すパラメータだけ違うインターフェースがたくさんある場合、イベントハンドラをどう書くかで困った。
onchange属性を各インターフェースにつければいいって話なんだけど、erbファイルとは別のファイルに書いたイベントハンドラを読み込むと、イベントハンドラがうまく発火しない。
onchange属性に書かれたものは、erbファイル内に書いたイベントハンドラであればうまく動くのだが、webpackerを導入していた && npmでインストールしたライブラリを使いたいこともあり、やはりerbファイルとは別のファイルにイベントハンドラを書きたかった。

はじめはイベントハンドラを書いたファイルの中で

const selectElms = document.getElementsByName('published')

Array.from(selectElms).forEach((elm) => {
  elm.addEventListener('change', (e) => someEventHandler(e))
})  

としていたのだが、forEachが入るのがめんどくさい。

そこで、

const selectParent = document.getElementById('selectParent')

selectParent.addEventListener('change', (e) => someEventHandler(e))

というように、bubbling phaseで処理するようにすることでforEachで回す必要がなくなりコード的にすっきりさせることができた。

これまでは、イベントの伝播はすぐ止めるようにしてたので伝播することのありがたみってよくわからなかったが、今回はじめてそのありがたみが少しわかった気がする。