Rick
Rick

Reputation: 1073

Rails POST method from two separate pages

I have a form for the same controller and action on two different pages and want the validation on the model to apply in both locations but I cant seem to get the render/redirect to work for both.

The default controller:

def create
  @job = Job.new(job_params)

  if @job.save
    redirect_back fallback_location: jobs_path, flash: { success: 'New job was added.' }
  else
    render :new
  end
end

This works when a new job is added, but if it fails it will always redirect back to the jobs/new route.

I have tried using the redirect_back method but this does not show the validation and current values are lost. I have had a play with looking at the referrer path and trying to extract the controller and method and re-render that but I have not had much success.

I would like to avoid having to add custom logic for each route and dont want a new action for each page I want to be able to create a job on.

Upvotes: 0

Views: 87

Answers (2)

Rick
Rick

Reputation: 1073

So I used Ryan's suggestion and set up a JS response. Posting here for future readers.

Controller:

# app/controllers/jobs_controller.rb
def create
  @job = Job.new(job_params)

  if @job.save
    redirect_back fallback_location: jobs_path, flash: { success: 'New job was added.' }
  else
    respond_to do |format|
      format.html { render :new }
      format.js
    end
  end
end

View:

# app/views/jobs/create.js.erb
var errors = <%= raw @job.errors.to_json %>;
validation.parseErrors('job', errors);

JS:

# app/assets/javascripts/client_validation/client_validation.coffee
@validation =
  parseErrors: (model, errors)->
    validation.clearErrors()
    for field, errorList of errors
      $field = $ "\##{model}_#{field}"
      $field.closest('.form-group').addClass 'has-danger'
      for error in errorList
        $field.after "<div class='form-control-feedback'>#{field} #{error}</div>"
  clearErrors: ->
    $('.has-danger').removeClass 'has-danger'
    $('.form-control-feedback').remove()

I am still not certain that this is the right way of doing this but it works quite nicely and much better than a simple js alert.

Upvotes: 0

Ryan K
Ryan K

Reputation: 4053

The way I usually do this is by submitting the form via JavaScript. That way, if it fails, I can just render an alert() with whatever error message there is. The values are still there (as I haven't left the page). If it succeeds, I just issue a window.location to the next page I want to go.

To do that, add the remote: true option to your form_for/form_tag. Then in your controller:

if @job.save
  render js: "window.location = 'new/path/after/save'"
else
  render js: "alert(\"#{@job.errors.full_messages.first}\")"
end

Upvotes: 1

Related Questions