Railsのサーバサイドだけで解決している編
Railsで例えばこういうデータがあったとして、
positive_phrasesテーブル
text |
頑張る人は報われる |
ここで諦めちゃダメだ |
君ならやれる |
さあ、もう一度トライしよう |
view側でこの文言を装飾したいとすると、
- @positive_phrases.each do |positive_phrase|
tr
td
= in_social_game_context(positive_phrase.text)
def in_social_game_context(text)
"#{text} ※ソーシャルゲームのガチャの話です"
end
こういうヘルパーを作ったら、以下のような表示がされます。
ここまでは楽勝ですね。
より良いUXのためにデータをページ遷移無しで追加/更新できるようにするとか、コンテンツ量が多い時のpagenationを「次へ」ボタンを押させるのではなく、スクロールに応じて動的に追加していく(いわゆる無限スクロールってやつですね)などの要求が出てくるケースはよくあると思います。
そういうことをやろうとすると、JavascriptでAPIをコールしてJSONで値を取得して要素を追加するというやり方になりますよね。
例えばこういう感じに。
positive_phrases/index.html.slim
input#new-phrase type="text"
button#submit-by-jquery
| Create Phrase by jQuery
positive_phrases.coffee
$("#submit-by-jquery").on("click", ->
$.ajax(
type: "POST"
url: "/positive_phrases.json"
data:
positive_phrase:
text:$("#new-phrase").val()
success: (data) ->
$("<tr><td>#{data.text}</td></tr>").appendTo($("#tbody"))
#$("<tr><td>#{in_social_game_context(data.text)}</td></tr>").appendTo($("#tbody"))
error: (a, b, c) ->
alert("error")
dataType: "json"
)
)
これで取得してきたJSONをHTMLに追加しようとした時に、Javascriptからin_social_game_context
というRailsのhelperは呼べないんですよね。
$("<tr><td>#{data.text}</td></tr>").appendTo($("#tbody"))
#$("<tr><td>#{in_social_game_context(data.text)}</td></tr>").appendTo($("#tbody")) => これが呼べない
↓
まあこれぐらいならJavascript側でも"※ソーシャルゲームのガチャの話です"って追加してやるだけで解決するわけですが、以下のような二つの問題が生じます。
1つ目の問題
その後の仕様変更で「※ソーシャルゲームのガチャの話です」を「※ただしイケメンに限る」に変えましょう、という話になりました。
そういうときにRails側だけ変更して、Javascript側は「※ソーシャルゲームのガチャの話です」になったままで、いっけなーいって気づくようなことってよくあると思います。2重メンテを担保し続けるのはハード。
inSocialGameContext = (text) ->
"#{text} ※ソーシャルゲームのガチャの話です"
...
success: (data) ->
$("<tr><td>#{inSocialGameContext(data.text)}</td></tr>").appendTo($("#tbody"))
...
JS側で同じようなメソッドを作って呼んでやるが、仕様変更の対応を忘れて片方だけ直した、みたいになっていると
(Rails側のView)
= but_only_handsome(positive_phrase.text)
JSで取得した部分だけ違う表示になってしまう。
2つ目の問題
片方を修正忘れする問題の以前に、Viewに書くhelperやdecoratorはこの例よりももっと複雑なケースが多いと思います。
特にRailsだと便利なGemで表示フォーマットを1メソッド呼ぶだけで複雑な整形を容易に実現している、というケースがあって、Javascriptだとそれを自前で実装しないといけないというケースは多いんじゃないかなと。
RailsとJavascriptの両方で同じ表示テンプレートを用意してやるのが手間ですね。
と言っても
ダブルメンテつらいけどやるしかないのか?と思っているのが現状。
他に考えられる案を挙げて見ると、
=> それが本質的な解決策ならいいですが、そうじゃないケースが多いはず
=> レンダリングのテンプレート組み立ては一箇所で済むのですが、Javascriptだと表示してから描画されることになるのでSEO評価されない可能性がありそう(※)です。これまた受け入れてもらえないケースが多そうですね。
(※)そこでReactのサーバーサイドレンダリングですよ、的な話はちょっと脱線するのであえて触れません
そうなると、Railsでのレンダリング、JSでのレンダリングの両方やらなきゃいけないのがつらいところなのかなと。
remote: true
とjs.erbを使うという手段もありますが、こちらは以下のような問題があるのかなあと思っています。
- formでしか使えない(?)
- 今回の例で言うとアイテムの追加には対応できるけど、無限スクロールでの要素追加なんかには対応できなさそう
- レンダリングしたDOMをHTMLに挿入する、という仕組みはjQueryではないちゃんとしたJSフレームワークとは相性が悪そう
- vue.jsにModel管理させて、あとはtemplateで描画してほしいねん・・・
この辺り、皆さんどうやってうまく解決しているのか気になるところですね。