Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent rails from changing the path when the edit action is rendered from update action

As far as I'm aware this is the standard rails pattern for editing and updating a resource.

I have a GET route for the edit action on /users/:id/edit and both a PUT and PATCH route for the update action on /users/:id.

Here are my controller actions:

def edit
  @user = User.find params[:id]
end

def update
  @user = User.find params[:id]

  if @user.update_attributes(user_params)
    redirect_to @user, success: "Changes saved"
  else
    flash.now.alert = "Unable to change account details"
    render :edit
  end
end

And I'm using the default form_for in my form partial:

<%= form_for @user do |f| %>

which is creating the following form in the html:

<form accept-charset="UTF-8" action="/users/1" id="edit_user_1" method="post">
  ...
  <input name="_method" type="hidden" value="patch">

When the call to update_attributes succeeds the user is being redirected correctly, however when the call fails (due to ActiveRecord validations) the render action displays the edit view correctly, but the path in the user's browser changes from /users/1/edit to /users/1.

From what I've read it appears this is the expected behaviour of rails, but that seems confusing to me as I thought the core idea behind REST was that a URL referred to a canonical view of a resource?

If I redirect_to to the edit action instead of render then the url is as it should be, however I lose the error messages on the form.

Is there a more sensible way to persist the URL and the error messages than dumping the errors into the session before using redirect_to? I'd like to avoid that if I can.

Update:

The reason I want the URL to persist (as well as the errors) is because I'm using current_page? to set active states in the navigation. Therefore the 'Edit' action in the menu loses it's highlighting if the form is submitted with errors.

like image 393
tommarshall Avatar asked Sep 04 '13 13:09

tommarshall


1 Answers

The reason you were in /users/1 is the view is rendered by #update, not #edit.

By default resource the path of #update is same as #show, with different request method GET and PUT. So, when #update renders HTML response, it's same as #show.

I think the result is acceptable, no matter what path your are in the result is expected. As you already found, if using redirect, the @user instance will be changed to a branch new one thus losing errors and previous fillings.

There is workaround on the path such as using session to pass instance variable, but I don't think it worth the effort. The current result is good enough.

Add

Manipulating active states is easy. I don't like current_page? which needs too much code. Use controller_path and action_name instead.

  if controller_path == 'users' && (action_name == 'edit' || 'update') 
     # add active class
  end      
like image 92
Billy Chan Avatar answered Nov 15 '22 08:11

Billy Chan