0

I have a helper which instantiates a model and renders a form. This form should be available to any view in the application

# support_form_helper
def support_form
  @support_stats = SupportForm::Stat.find(get_stats_id)
  @enquiry = SupportForm::Enquiry.new(stats_id: @support_stats.id)
  render partial: 'support_form/enquiries/form'
end

And its rendered in the view:

# some_view.html.erb

<%= support_form %>

This is fine until I want to submit the form and validate it in the controller.

# enquiries_controller.rb
def create

  @enquiry = SupportForm::Enquiry.new(params[:support_form_enquiry])

  topic = @enquiry.topic
  @stat = SupportForm::Stat.find(@enquiry.stats_id)

  @stat.stats[topic] = @stat.stats[topic].to_i.next
  respond_to do |format|
    if @enquiry.valid? && @stat.save
      format.html { redirect_to(root_path) }
    else
      format.html { redirect_to(:back) }
    end
  end
end

This is where I can't render the previous view with the errors attached to the invalid object. The helper gets invoked again and initializes a new @enquiries object, without the errors obviously.

How can I render the form in many views across an application and still return to the view with the object together with errors when it is invalid?


I found an answer which answers my question but its a bad idea:

Render the action that initiated update

def create @enquiry = SupportForm::Enquiry.new(params[:support_form_enquiry])

  topic = @enquiry.topic
  @stat = SupportForm::Stat.find(@enquiry.stats_id)

  @stat.stats[topic] = @stat.stats[topic].to_i.next

  if @enquiry.valid? && @stat.save
    redirect_to(root_path)
  else
    render Rails.application.routes.recognize_path(request.referer).values.join("/")
  end
end

The problem is that there will likely be instance variables in the view that submitted the form and I would have to be able to instantiate all the instance variable in the application then.....not possible.

Currently I'm considering putting the errors in the flash hash... not something I want to do. With the original object returned i can repopulate the fields with the users input.

Community
  • 1
  • 1
iNulty
  • 923
  • 8
  • 17

1 Answers1

0

When you use redirect_to, rails will kick off a whole new controller & view sequence. Use

render "path/to/template/from/view/folder"`

instead.

A typical create action using this pattern would look like (for a 'post' object in this case):

def create
  @post = Post.new(params[:post])
  @created = @post.save
  respond_to do |format|
    if @created
      flash[:notice] = 'Post was successfully created.'
      format.html { redirect_to post_path(@post) }
      format.js
    else
      format.html { render :action => :new }
      format.js
    end
  end
end 

Notice how if it's successfully created we do a full redirect to the "show" page for the post, but if it's not successful we just do a render.

You should probably modify your support_form helper so that it only creates a new @enquiry if it hasn't been created already:

def support_form
  @support_stats = SupportForm::Stat.find(get_stats_id)
  @enquiry ||= SupportForm::Enquiry.new(stats_id: @support_stats.id)
  render partial: 'support_form/enquiries/form'
end

||= is shorthand for "equals itself or". If it hasn't been defined (or is nil or false) then it will fail the first part of the or and pass through to the second, where the object is created.

In your form partial, also, you should make sure you're using form_for, which will submit to the create or update action depending on whether the object has been saved already.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • My problem is that I can't pass the ``@enquiry`` from the controller to the helper. I've tried the "equals itself or" but @enquiry is always nil. Also, I am aware to redirect_to and render. I don't kno what controller or action I'll have to render though so ``redirect_to :back`` seemed a logical solution passing the object back with the redirect – iNulty Feb 06 '14 at 16:16
  • When you say "@enquiry is always nil" do you mean after doing a redirect? Whenever you redirect, you start from scratch and all instance vars are wiped out. if you render then you should still have it. – Max Williams Feb 07 '14 at 10:36
  • I can render with that piece of code I refer to in the question (Render the action that initiated update) but then I need to set the instance variables that might be set by that actions controller. Say, the pages#show action for instance..I still need to set the instance variables that the pages#show action sets from the enquiries#create action when I need to return to the action that rendered the form. Maybe if I just render an enquiries#new action and drop the last pages controller altogether. Not good for the user though. At this point I'm looking at redirecting with a cookie set. – iNulty Feb 07 '14 at 10:45