0

I'm working on creating a survey app where surveys can have many questions and questions can have many surveys. What I'd like to do is on the survey show page display a button "Add new question" that allows a user to add a new question to that survey. So in my code I send the survey id like this:

<%= link_to "Add Question", new_question_path(:survey_id => @survey.id)%>

Then I can set @survey using the params I'm sending in my question controller. This is working fine in my :new method, but is throwing a nil error when I try to call in the :create method. I believe this is because a new instance of the controller is getting created which no longer has access to the :survey_id param I sent initially.

So I'm wondering if there is anyway to pass along the params to the next instance of the controller? Or is there a better way to send which survey should be set for that question? Is this something I could "save" in a hidden field? I thought about trying to save something in my model, but to save a question earlier would require me to remove the validations I have.

Here's my question_controller:

class QuestionsController < ApplicationController
  before_action :set_question, only: [:show, :edit, :update, :destroy]
  before_action :set_survey, only: [:new, :create]
  # GET /questions
  # GET /questions.json
  def index
    @questions = Question.all
  end

  # GET /questions/1
  # GET /questions/1.json
  def show
    @answers = @question.answers
  end

  # GET /questions/new
  def new
    @question = Question.new
  end

  # GET /questions/1/edit
  def edit
  end

  # POST /questions
  # POST /questions.json
  def create
    @question = Question.new(question_params)

    respond_to do |format|
      if @question.save
        @survey.questions << @question 
        format.html { redirect_to @question, notice: 'Question was successfully created.' }
        format.json { render action: 'show', status: :created, location: @question }
      else
        format.html { render action: 'new' }
        format.json { render json: @question.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /questions/1
  # PATCH/PUT /questions/1.json
  def update
    respond_to do |format|
      if @question.update(question_params)
        format.html { redirect_to @question, notice: 'Question was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @question.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /questions/1
  # DELETE /questions/1.json
  def destroy
    @question.destroy
    respond_to do |format|
      format.html { redirect_to questions_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_question
      @question = Question.find(params[:id])
    end

    def set_survey
      @survey = Survey.find(params[:survey_id])
      flash[:alert] = "Survey is " + @survey.to_s
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def question_params
      params.require(:question).permit(:title, :single_response, :surveys, :surveytizations)
    end
end

And the form I'm creating the question with:

<%= form_for(@question) do |f| %>
  <% if @question.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@question.errors.count, "error") %> prohibited this question from being saved:</h2>

      <ul>
      <% @question.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :single_response %><br>
    <%= f.check_box :single_response %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Thanks! Any help is very much appreciated!

UPDATE:

I was able to work using Rails.cache.write/Rails.cache.read - How to pass values between controller methods

Is there anything wrong with doing it that way or is that the best route?

4
  • The best is to use nested routes. Cache is temporary so there are still a chance of nil, if the cache is flush. And depending on how you cache it, other users may get the wrong survey id. Commented Feb 23, 2014 at 20:49
  • I was initially using nested routes for surveys > questions, but then started running into a lot of issues when I nested questions > answers. You wouldn't happen to know of a good tutorial or resource for how to handle the double nested routes/forms would you? Commented Feb 23, 2014 at 20:54
  • For the answers you dont need double nested route, so you can define two resources of question, one that nested under survey and one that is not. The one that is not will still hold the resource for answers Commented Feb 23, 2014 at 20:59
  • That is super cool, I had no idea you could do that! Commented Feb 23, 2014 at 21:09

1 Answer 1

1

I think you need to store the survey_id in a hidden field. Then you can access it from the questions controller. In your view:

<%= form_for(@question) do |f| %>
  <%= f.hidden_field :survey_id %>
#rest of form

You also might have to change your new action to something like this:

@question = Question.new(:survey_id => params[:survey_id])

If the questions always belongs to a survey it could be a good idea to nest the routes so that you always can check which survey you are working on.

Sign up to request clarification or add additional context in comments.

2 Comments

Could you give an example of what it would look like to add it in the hidden field? I haven't been able to find a good example. I initially was nesting the question. But then I was running into problems with the nested answers within the questions and opted to separate them
Also, if you are letting users create surveys and questions you probably want to check that the survey_id does not belong to another user.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.