I've successfully been able to implement this tutorial up to a point.
I can get the nested items to appear by saving the parent form and going back to edit, but I am unable to get my Delete or my Add Option button to actually work.
The Delete button doesn't appear to actually do anything and I can't understand the server stream to figure out what it is trying to do.
The Add Option button somehow is returning "unpermitted parameter" for :title, :description, :question_options_attributes error by attempting to create a parent record instead of a child one as it goes up one level in the controller chain to the surveys/questions_controller#create action.
here's what I have so far:
class SurveyQuestion < ApplicationRecord
belongs_to :survey_section
has_many :question_options
accepts_nested_attributes_for :question_options, reject_if: :all_blank, allow_destroy: true
class Surveys::QuestionsController < SurveysController
before_action :get_section, only: [:index, :new, :create, :update, :edit]
before_action :set_question, only: [:edit, :update]
...
def edit
@option = @question.options.build
render partial: "surveys/questions/edit", locals: {q: @question,
survey: @survey, section: @section, option: @option}
end
private
def get_section
@survey = Survey.find_by(slug: params[:survey_slug])
@section = @survey.sections.find_by(section_number: params[:sec_num])
end
def create_params
params.require(:survey_question).permit(:survey_section_id, :position, :question_type)
end
def update_params
params.require(:survey_question).permit(:survey_setion_id, :position, :question_type,
:title, :description, question_options_attributes: [:id, :_destroy, :name,
:logic, :sub_logic])
end
def set_question
@question = @section.questions.find_by(position: params[:position])
end
In my survey question edit view:
%turbo-frame{id: "question_#{q.id}", data: {turbo: {action: "advance"}}}
= form_with(model: q, url: survey_question_path(survey_slug: survey.slug, position: q.position, sec_num: section.section_number)) do |f|
...
%turbo-frame{id: "options"}
= f.fields_for :question_options, q.options do |of|
= render "surveys/questions/options/form", form: of, q: q
.text-start
= f.submit "Add Option", formaction: survey_question_option_path(survey_slug: q.survey_section.survey.slug, question_position: q.position, index: q.options.size, sec_num: q.survey_section.section_number), formmethod: :post, formnovalidate: true, id: "add_option"
in the surveys/questions/options/form partial:
%turbo-frame{id: "question_#{q.id}_option_#{form.index}"}
= form.hidden_field :id
%p This is form index number #{form.index}.
.form-floating.mb-4
= form.text_field :name, placeholder: "Option #{form.index + 1}", class: "form-control border border-dark"
= form.label :name, "Option #{form.index + 1}"
= form.submit "Delete", formaction: survey_question_option_path(survey_slug: q.survey_section.survey.slug, question_position: q.position, index: form.index, id: form.object.id, sec_num: q.survey_section.section_number), formmethod: :delete, formnovalidate: true, data: {turbo: {frame: "question_#{q.id}_option_#{form.index}"}}
in the surveys/questions/options/create.turbo_stream.haml:
= fields model: @question do |f|
= f.fields_for :question_options, @options, child_index: params[:index] do |of|
= turbo_stream.replace "add_option" do
= f.submit "Add Option", formaction: survey_question_option_path(survey_slug: @survey.slug, question_position: @question.position, index: of.index.to_i + 1, sec_num: @section.section_number), formmethod: :post, formnovalidate: true, id: "add_option"
= turbo_stream.append "options" do
= render "surveys/questions/options/form", form: task_form, q: @question
in the surveys/questions/options/destroy.turbo_stream.haml partial:
= fields model: @question do |f|
= f.fields_for :question_options, @options, child_index: params[:index] do |of|
%turbo-frame{id: "question_#{@question.id}_option_#{of.index}"}
= of.hidden_field :id, value: params[:id]
= of.hidden_field :_destroy, value: true
= turbo_stream.remove(id: "question_#{@question.id}_option_#{params[:index]}")
the options controller:
class Surveys::Questions::OptionsController < Surveys::QuestionsController
before_action :setup
def new
end
def destroy
end
private
def setup
@survey = Survey.find_by(slug: params[:survey_slug])
@channel = @survey.channel
@section = @survey.sections.find_by(section_number: params[:sec_num])
@question = @section.questions.find_by(position: params[:question_position])
@options = @question.options
end
end
And finally the relevant routes:
resources :surveys, param: :slug do
resources :sections, controller: "surveys/sections", param: :sec_num
resources :questions, controller: "surveys/questions", param: :position do
resources :options, controller: "surveys/questions/options", only: [], param: :index do
member do
delete "(:id)" => "surveys/questions/options#destroy", as: ""
post "/" => "surveys/questions/options#create"
end
end
end
end
Did I miss something?