Railsで質問種別ごとの動的フォームを作成する(with turbo)
Nested Forms With Turbo (without dependencies)で紹介されている動的フォームに質問種別の切り替えを追加する。
課題
質問種別を選択し、選択した質問種別でtextやcheckboxといった動的フォームを切り替えたい
Javascriptなしで動的フォームを作成する - formaction,turbo_stream
質問種別を選択するselect_tagを追加することで動的フォームを切り替えることができる。
#app/views/surveys/_form.html.erb
...
<div id="questions"></div>
<%= select_tag "question_type", options_for_select([["Text", "text"], ["Multiple Choice", "multiple_choice"]]) %>
<%= button_tag "Add Question", formaction: new_question_path, formmethod: :get, data: {turbo_stream: true} %>
...
QuestionController#newのturbo_stream内で、レンダリングするviewを切り替える。
# app/views/questions/new.turbo_stream.erb
<%= turbo_stream.append "questions" do %>
<%= fields_for 'survey[questions_attributes][]', Question.new, index: Time.current.to_i do |form| %>
<%= render "surveys/question_fields", form: form %>
<% end %>
<% end %>
# app/views/surveys/_question_fields.html.erb
<div>
<%= params[:question_type] %>
<%= form.label :content, "Question" %>
<% if params[:question_type] == "multiple_choice" %>
<%= form.check_box :content %>
<%= form.check_box :content %>
<%= form.check_box :content %>
<% else %>
<%= form.text_area :content %>
<% end %>
</div>
現状では、button_tag
にformactionとformmethodを利用してformタグが指定するリソースを上書きしている。
そのため、form_with内のパラメータがすべてGETリクエストで送られる。
Started GET "/questions/new?_method=patch&authenticity_token=[FILTERED]&survey%5Bname%5D=1&survey%5Bquestions_attributes%5D%5B1740783204%5D%5Bcontent%5D=1111&question_type=multiple_choice&button=" for 127.0.0.1 at 2025-02-28 22:53:26 +0000
Processing by QuestionsController#new as TURBO_STREAM
Javascriptありで動的formを作成する - javascript,turbo_stream
formactionによるリソースの上書きでは、不要なパラメータがサーバー側に送信される。
2000文字を超えるURLは危険と考えられているため、代替案を検討する。
buttonタグによるformactionの書き換えから、aタグのURL生成へ実装方針を切り替える。
aタグに変更し、select_tag変更時にaタグのクエリパラメータを差し替える。
- <%= select_tag "question_type", options_for_select([["Text", "text"], ["Multiple Choice", "multiple_choice"]]) %>
- <%= button_tag "Add Question", formaction: new_question_path, formmethod: :get, data: {turbo_stream: true} %>
+ <fieldset class="contents" data-controller="search-params">
+ <%= select_tag "question_type", options_for_select([["Text", "text"], ["Multiple Choice", "multiple_choice"]]), data: {action: "search-params#encode"} %>
+ <%= link_to "Add Question(LINK)", new_question_path, data: {turbo_stream: true, search_params_target: "anchor"} %>
+ </fieldset>
URLSearchParamsを利用して、select_tagで選択している値をクエリパラメータに設定する。
// app/javascript/controllers/search_params_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "anchor" ]
encode({ target: { name, value } }) {
for (const anchor of this.anchorTargets) {
anchor.search = new URLSearchParams({ [name]: value })
}
}
}
question_typeのみを送信してformを切り替えることができるようになった。
Started GET "/questions/new?question_type=multiple_choice" for 127.0.0.1 at 2025-03-01 01:03:01 +0000
Processing by QuestionsController#new as TURBO_STREAM
Parameters: {"question_type"=>"multiple_choice"}
ラップアップ
javascriptとturboを使うことで、不要なパラメータ送信することなく質問種別毎に動的フォームを切り替えることができた。
turboと組み合わせることで、javascirptはURLのクエリパラメータを設定するのみでよい。これまでjavascriptがAPIエンドポイントへリクエストをし、HTMLレンダリングしていたコードが不要となる。
参考
Discussion