I am following this tutorial to create a multi-step form using the Wicked gem. Each User has one Answer (model to store answers to a set of questions), and I have a QuestionnaireController that steps through the questions that the user will answer and have stored in Answer. So far I have just two steps, each with one question (boolean columns on the answers table). When I step through and answer each question everything works perfectly, however when I submit without a value I get:
param is missing or the value is empty: answer
My code...
answer.rb:
class Answer < ApplicationRecord
belongs_to :user
cattr_accessor :questionnaire_steps do
%w(step_one step_two)
end
attr_accessor :current_step #stores the current questionnaire step
validates :user_id, presence: true
with_options if: -> { required_for_step?(:step_one) } do |step|
step.validates :question_one, inclusion: [true, false]
end
with_options if: -> { required_for_step?(:step_two) } do |step|
step.validates :question_two, inclusion: [true, false]
end
def required_for_step?(step)
# All fields are required if no questionnaire step is present
return true if current_step.nil?
# All fields from previous steps are required if the
# step parameter appears before or we are on the current step
return true if self.questionnaire_steps.index(step.to_s) <= self.questionnaire_steps.index(current_step)
end
end
answers_controller.rb:
class AnswersController < ApplicationController
before_action :setup
def setup
@user = current_user
end
def show
@answer = @user.answer
end
def new
@answer = Answer.new
@answer = @user.build_answer
@answer.save(validate: false)
end
def create
@answer = @user.answer
redirect_to questionnaire_path(@answer, Answer.questionnaire_steps.first)
end
end
questionnaire_controller.rb:
class QuestionnaireController < ApplicationController
include Wicked::Wizard
before_action :setup
steps *Answer.questionnaire_steps
def setup
@user = current_user
end
def show
@answer = @user.answer
render_wizard
end
def update
@answer = @user.answer
@answer.update_attributes(answer_params(step))
render_wizard @answer
end
private
def answer_params(step)
permitted_attributes = case step
when "step_one"
[:question_one]
when "step_two"
[:question_two]
end
params.require(:answer).permit(permitted_attributes).merge(current_step: step)
end
end
step_one.html.erb:
<div id="questionnaire" class="container-fluid well inline-headers">
<%= form_for @answer, method: :put, url: wizard_path do |f| %>
<div class="row">
<% if f.object.errors.any? %>
<div id="error_explanation" class="alert alert-dismissible alert-danger" >
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<ul>
<% f.object.errors.each do |attribute, message| %>
<%= content_tag :li, f.object.errors.full_message(attribute, message), :id => "error_#{attribute}" %>
<% end %>
<ul>
</div>
<% end %>
</div>
<div class="row">
<div class="col-md-8 form-group">
<p>Are you any good at this?</p>
</div>
<div class="col-md-4 form-group">
<%= label_tag(:question_one, "Yes") %>
<%= f.radio_button :question_one, true %>
<%= label_tag(:question_one, "No") %>
<%= f.radio_button :question_one, false %>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8 form-group">
<p><%= f.submit "Save & Continue", class: "btn btn-warning" %></p>
</div>
</div>
<% end %>
</div>
routes.rb (admittedly I tried to get cute with these to mask the IDs, feel free to shame me into doing this the right way):
resources :answers, only: [:show, :new, :create]
get 'user/questionnaire', to: 'answers#show'
get 'user/questionnaire/new', to: 'answers#new'
post 'user/questionnaire/new', to: 'answers#create'
resources :questionnaire
When I select an option and submit, it saves to the database and redirects to the next step, however if I submit without selecting an option I get the param is missing error when I expected to get validation errors. From the server log:
Processing by QuestionnaireController#update as HTML Parameters: {"utf8"=>"���", "authenticity_token"=>"J45sG/YIqTaBECgBqNLr9Ys3rJewDK78d/cKHWFHYEIzAaOiTXhgH6evPZKl5LhnSAMnQnA94CXonqriyj0fCQ==", "commit"=>"Save & Continue", "id"=>"step_one"}
I have tried a number of potential fixes, including removing require(:answer)
from params.require(:answer).permit(permitted_attributes).merge(current_step: step)
, which gets me the validation error I was expecting but then doesn't allow saving a valid value. In that scenario I still get the validation error with the following in the server log:
Started PUT "/questionnaire/step_one" for ::1 at 2016-12-08 23:26:14 +0545 Processing by QuestionnaireController#update as HTML
Parameters: {"utf8"=>"���", "authenticity_token"=>"RWAHfu0BE82ik6gTChh3nmtuyOUa5j0qiz4H03ejIkRR78jHVnHa5IQsvYAHLiQMqFpDMNrXc/MUV6cs3NldDw==", "answer"=>{"question_one"=>"true"}, "commit"=>"Save & Continue", "id"=>"step_one"} ...SQL... Unpermitted parameters: utf8, _method, authenticity_token, answer, commit, id
Many thanks in advance for helping out a rookie.
UPDATE: I modified my routes to match the tutorial (nested) thinking this might have something to do with it (i.e. maybe not getting the answer ID) but I'm still getting the param is missing error.
resources :answers, only: [:show, :new, :create] do
resources :questionnaire, only: [:show, :update], controller: 'questionnaire'
end
get 'user/questionnaire', to: 'answers#show'
get 'user/questionnaire/new', to: 'answers#new'
post 'user/questionnaire/new', to: 'answers#create'
I also tried adding a hidden_field_tag before the submit, like so:
<%= hidden_field_tag(:answer, @answer) %>
But then I get:
undefined method `permit' for #
Probably because the hidden_field_tag passes @answer.attributes as a string when the strong parameter declaration expects a hash. From the server:
Started PUT "/answers/46/questionnaire/step_one" for ::1 at 2016-12-12 13:25:19 +0545 Processing by QuestionnaireController#update as HTML Parameters: {"utf8"=>"���", "authenticity_token"=>"wlLpKoMvqgyfwB5gyP/qubR4ZpZa6jm9KHeMuiwJTc6IOJZi9PgiSegvF+OzlN09ctEuxuxiAuRs+/B2InRGhA==", "answer"=>"{\"id\"=>46, \"user_id\"=>4, \"question_one\"=>nil, \"question_two\"=>nil, \"percentage_complete\"=>#, \"completed\"=>false, \"created_at\"=>Mon, 12 Dec 2016 02:12:31 EST -05:00, \"updated_at\"=>Mon, 12 Dec 2016 02:12:31 EST -05:00}", "commit"=>"Save & Continue", "answer_id"=>"46", "id"=>"step_one"}
Please help!
I changed params.require
to params.fetch
and that works (validations are triggered appropriately and I can go through the steps with valid values successfully as well).