4
4

More than 3 years have passed since last update.

僕「えっ?HTMLの生成はRailsにさせて、jqueryは画面に当て込むだけにする?」

Posted at

jquery+Railsのプロジェクト進行中僕

僕「今日も今日とて、業務システム開発案件の実装だわ。」

上司(50歳独身)「おっ、僕。おはようさん!」

僕「上司さん。今日もハゲてますね。」
(おはようございます!今日も素敵なネクタイですね!)

上司(50歳ハゲ)「ハハ。また建前と本音が逆になってるぞ。次やったら最果て支社に飛ばすからな。」

僕「あぁ。今回もフロント側がjqueryかぁ・・・たまにはvue.jsとかreactとか使ってみたいなぁ・・・」

上司「無視すんな。」

イケメン後輩「僕さん。そんなjquery嫌なんですか?なんでですか?」

僕「おっ。社内でも何にでも挑戦するともっぱら評判のイケメンじゃん。いやぁ、jqueryって何か知らんけど、vue.jsとかみたいにコンポーネント指向で共通化したコードを書けないし、状態の監視も厳しいし、ゴリゴリ書かないといけないんでしょ?」

上司「おい。無視すんな。」

イケメン「確かに。vue.jsとは思想が違いますからね。そもそも向こうは仮装DOMで、jqueryはDOM操作ですしね。確かに普通にやったら、vue.jsとかみたいに、コンポーネントを作ってそれを組み合わせてっていうことはしないかもしれませんね。」

僕「そうでしょ。だからRails使うのはいいけど、jqueryだとテンションが上がらないんだよなぁ・・・。しかも、jqueryだとひっどい書き方されると、物凄くコードが長くなったり、どこに何が書いてあるかわからなくなるんだよ・・・」

イケメン「確かにjqueryでゴリゴリ書くのは辛いものがありますよね。でも、共通化も面倒なDOM生成とかもRailsの力を借りて、jqueryをただの画面への反映装置みたいに使うっていう考えもあるんじゃないですか?」

僕「えっ?」

上司「あっ。これずっと無視される流れだ。」(´·ω·`)

イケメン「Railsの部分テンプレート機能とか、view_helperを使って、それを動的にページに反映していけば、jqueyでゴリゴリ書くのは抑えられると思いますよ。」

ちくわ大明神「それもまた一興」

イケメン「実際のところこの考え方が正しいかは分かりませんが、ちょっと見てもらえますか?」

僕「何それ。教えて。」

上司「おい!さっきの誰だ!」

※ここに記載するやり方が、効率面・セキュリティ面で正しいのか、分りません!業界1年未満のものが書いているものですので、逆に色々教えていただけると幸いです!!本記事はワイ記法などの影響をもろに受けておりますが、ご容赦ください。

僕さん的jqueryで画面の動的更新手法(辛い)

イケメン「僕さん。Railsでviewの共通化といえば?」

僕「そのくらい分かるわ。部分テンプレート(パーシャル)でしょ?」

イケメン「そうです。パーシャルを使います。後、RailsでformとかのDOMを効率的に生成する方法といえば?」

僕「なめてんのか。↓みたいな、view_helperを使った記法とかやろがい。」

<%= form_with model:@boku, url:'/boku/createBoku' ,id:'boku_form' do |f| %>

<%= f.text_field :name %>
<%=  f.selext :seibetu,options_for_select( [[1,"男"],[2,"女"],[3,'イケメン']] )  %>

<% end >

イケメン「バカにしてないですよ!僕の案はこの2つを使って、かつ、画面を遷移せずに動的にdomを更新したらどうでしょう?って言いたいんです。」

僕「は?でも、パーシャルって↓みたいに、viewの中で呼び出すでしょ。だったら、最初に画面に描画して終わりじゃん。↓みたいな感じで。後はjqueryを使って画面を動的に更新していくだけだろ。」

<%= render partial: 'form', locals: { boku: Boku.new } %>

イケメン「じゃあ、ページ遷移せずに、サーバーのデータを使って、動的にDOMを生成したり、更新したりするには、どうされてます?」

僕「そりゃajaxで↓みたいに。」

#rails contoroller
#viewに表示される性別用のセレクトボックス変更時に、選ばれた性別で絞り込んだデータを動的にサーバーからもらうイメージです。

def select_data_return
    return_data = Boku.where(seibetu: params[:seibetu])map{ |boku| {id :boku.id,name: boku.name} }
    render json:{data: return_data},status: 200 and return
end


$('#seibetu').on('change',function(){
    $.ajax({
        url: '/boku/select_data_return',
        type: 'GET',
        dataType: 'json',
        data:{
            seibetu: $('#seibetu').val()
        }
      })
      .done(function(json){
        //受け取ったデータをdomに変更して、画面に反映
        const data = json.data;
        let domArray = []
        data.forEach((elm)=>{
            domArray.push(`<div>id:${elm.id}、名前:${elm.name}</div>`);
        });
        ${'#target'}.html(domArray);
      });
})

イケメン「確かにこのくらい単純ならjquery側でdomを生成しても問題ないかもしれませんが、ボタンが押されるたびに入力用のフォームが作られて、ユーザーが任意の数のレコードのデータを一括保存(更新も)できるようにするとかだとどうするんですか?」

僕「業務システムにありがちだね。仕方ないね。そんな時は根性で・・・」

//jsファイル

$('#addBtn').on('click',function(){
    //formから飛ばすデータを "1" => {name: ''},"2" => {name: ''}みたいにするための番号をDOMに持っていて・・・ 
    const next_num = $('#max_num').val() + 1;
    add_dom = `
        <input type="text" name="boku[${next_num}][name]"
    `
    //〜みたいに手動でdomを生成する無間地獄。name属性やid属性の間違いは許されない
})

イケメン「先輩。それをBokuモデルの全30種類の列用の入力フォームを作っていくんですか?」

僕「うん。こんなんだから、jqueryだと変数の管理も大変だし、dom生成も大変だし、どこでうっかりname属性とかid属性とか間違えないかヒヤヒヤしなければいけないし、jquery嫌いなんだよ。」

イケメン「いや。それはjqueryが悪いのではなく、Railsの機能と上手く連携できていない先輩の方が悪いですよ。」

僕「でも、例えばoptions_for_selectで生成される、domを使いたくても、解釈してくれないじゃん。だったら、自分でformを作っていかないと仕方ないよね?↓みたいに。」

//ajax処理でサーバーからデータを受けとった後
.done(function(json){
    //jsonの中身 => [[1,"男"],[2,"女"],[3,'イケメン']]
    //僕さんは気持ち的にこう書きたい↓
    $(セレクトボックス).append(`<%= options_for_select(${json}) %>`);
    //当然jqueryではRailsのoptions_for_selectなんて解釈できないので、<option></option>は生成されない
});

イケメン「僕さんのいう通り、jqueryでは<%= %>記法もoptions_for_selectメソッドも解釈できません。Railsのメソッドですし。でも、俺さん。ajaxでサーバーからデータを受け取って、それをjqueryでhtmlタグに整形するくらいなら、最初からRails側で整形してくれたhtmlの文字列をjqueryで当てはめるだけでよくないですか?」

僕「え?どうやって?」

イケメンの案(DOMの動的生成もRailsの力を借りる)

イケメン「まず、options_for_selectを例にRails側でhtmlを整形してもらって、それをjqueryでviewに当てはめる例を見てみましょう。」

僕「ほーん。」

class BokuController < ApplicationController
    #js側でdomを生成せずにRailsで生成したdomを返せるようにhelperをinclude(正直これが正しいやり方かわからないです。)
    include ActionView::Helpers::FormOptionsHelper

    #Railsのコントローラー
    def select_data_return
        options = options_for_select(Boku.where(seibetu: params[:seibetu])map{ |boku| {id :boku.id,name: boku.name}})
        render json: options
        #optionsには↓みたいな文字列が格納されている
        #<option value="1">男</option><option value="2">女</option><option value="3">イケメン</option>
    end

//js
//ajaxでサーバーから受け取った<option>タグの文字列をselectボックスに当て込むだけ
.done(function(json){
    $(セレクトボックス).append(json);
});

イケメン「正直view_helperをコントローラーで使うことの是非は分かりません。でも、Railsの機能を上手く使えば、たったこれだけの行数でhtmlを書けるって良くないですか?」

僕「確かに行数は短くなるし、Railsのview_helperを使えるなら、ちゃんとした引数を渡せば、必ず一定の形に整えてくれるから、良いかも・・・」

イケメン「私的には、Railsの機能を活用できて、いいなぁと思っています。後、部分テンプレートを活用する例も見ていただけますか?」

僕「話の流れ的に、Railsのコントローラー側で、あらかじめ用意している部分テンプレートに必要な変数(プロパティ的な)を引き渡したものを、htmlの文字列に変えて、jsに渡すんかな?」

イケメン「おっしゃる通りです!↓みたいなイメージですね。」

<!-- templateを用意。 _form.html.erb -->
<!-- こんな感じでがっつりview_helperの力を借りる -->
<% if now_new_field_count %>
    <%= fields_for "boku_new[#{now_new_field_count}]", Boku.new do |boku| %>
        <%= boku.text_field :name ,class:"name_form" %>
        <%= boku.text_field :family_name ,class:"family_name_form" %>
        <%= boku.select :seibetu options_for_select(Boku.where(seibetu: params[:seibetu])map{ |boku| {id :boku.id,name: boku.name}}) %>
    <% end %>
<% end %>
<!-- <input type="text" name="boku_new[1][name]"> <select name="boku_new[1][seibetu]"></select>のようなhtmlタグを作ってくれるイメージ。 -->
<!-- templateを呼び出す側のview -->
<!-- form_withをここで用意しておく -->
<%= hidden_field_tag :now_new_field_count,now_new_field_count %>
<%= form_with model:Boku.new, url:'/boku/create_boku' ,id:'boku_form' do |f| %>
    <div id="boku-form-zone">
        <%= render partial: 'form', locals: { now_new_field_count: nil, } %>
    </div>
<% end %>
#controller
def form_dom_return
    next_field_count = params[:now_new_field_count].to_i + 1
    #ここで部分テンプレートをhtml文字列にRailsで変換してもらっている
    partial = render_to_string(partial:'form', :locals => { now_new_field_count: next_field_count })
    render json:{partial: partial,now_new_field_count: next_field_count}
end
//js側ajaxでrailsへリクエストを送り、帰ってきた値(Railsで生成したdomの文字列)を当て込むだけ
$('#addBtn').on('click',function(){
    $.ajax({
        url: '/boku/form_dom_return',
        type: 'GET',
        dataType: 'json',
        data:{
            //hidden_fieldにある値を送る
            now_new_field_count: $('#now_new_field_count').val()
        }
      })
      .done(function(json){
        $('#boku-form-zone'}.append(json.html);
        $('#now_new_field_count').val(json.now_new_field_count)
      });
})

僕「色々やっているように見えるけど、単にあらかじめ用意していた部分テンプレートを、controllerでhtmlに解釈してもらって、jqueryはそのまま当て込むだけってことなんやね。」

イケメン「そうなんですよ!これでRailsのview_helper、部分テンプレートの良さを活かしながら、動的にDOMを更新していくことができるんですよ!ちなみに、この例だとformでサブミットした時に、↓のような形でパラメーターが飛ぶので、一々jsで飛ばすパラメーターを成形したりする必要もありませんし、Railsでcreateするのも楽ですよね。」

//パラメータの飛び方イメージ

"boku_new" => {
    "1" => {
        "name" => "僕男",
        "family_name" => "", 
        "seibetu" => "1"
    },
    "2" => {
        "name" => "",
        "family_name" => "上司", 
        "seibetu" => "1"
    }
    "3" => {
        "name" => "",
        "family_name" => "イケメン", 
        "seibetu" => "3"
    }

}

そんなこんなで

僕「なるほど。確かにSPAとか色々出てきている中で、jqueryは古く見えるかもしれないけど、要は使い方で、せっかくRails使ってるんだから、その恩恵をガッツリ使ってしまえば良いっていう考えがあるのか。」

イケメン「まぁ。このやり方には色々と賛否もあられるとお思います。所詮、この業界に入って8ヶ月のあほ筆者が色々な人の実装を見て、書いたやり方ですから。」

僕「あぁ。文才もないアホ筆者がね。」

イケメン「ただ、view一つでも出来れば共通化しておけば、エラー時の原因の特定が容易になるし、手戻りが生じたときも修正しやすいと思っていますけどね。」

僕「なるほど。僕も参考にするわ。」

イケメン「あくまで意見の一つとして見てくださいね。」

僕「これで実装して何か不具合とか、不都合あったら全部お前のせいにするわ」(ハハッ。分かってるよ。あくまで参考にするだけさ。)

イケメン「クソ野郎ですね。」

僕「ところで、上司さんどこ行ったんやろ。」

イケメン「さぁ?」

その頃・・・

上司「くそっ。あのちくわみたいな顔している奴、見失った・・・」

ナレーター「上司とちくわ大明神の戦いは続く・・・」

上司「誰だお前。」


元記事:クソ筆者のブログみたいな備忘録みたいなもの

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4