How do I prevent URL in browser bar from changing when a user is returned to a form to correct errors?
Asked Answered
P

7

7

I 'm using Rails 5. I have a page where a user can update their profile and if something goes wrong, they are returned to the page

  def update
    @user = current_user
    if @user.update_attributes(user_params)
      …
      redirect_to url_for(:controller => ‘main’, :action => 'index') and return
    end

    render 'edit'
  end

The problem is, when they are returned to the original page, the URL in the browser bar reads, “http://localhost:3000/users/51”, which is not the original URL they were visiting (that was “http://localhost:3000/users/edit”). How can I get the URL to remain the same as what it was?

Edit: This is what is produced when I run rake routes

           edit_users GET    /users/edit(.:format)                      users#edit
                users GET    /users(.:format)                           users#index
                      POST   /users(.:format)                           users#create
             new_user GET    /users/new(.:format)                       users#new
            edit_user GET    /users/:id/edit(.:format)                  users#edit
                 user GET    /users/:id(.:format)                       users#show
                      PATCH  /users/:id(.:format)                       users#update
                      PUT    /users/:id(.:format)                       users#update
                      DELETE /users/:id(.:format)                       users#destroy
Pneumodynamics answered 2/1, 2017 at 23:22 Comment(4)
and return is a bad habit to get into as there's no guarantee that things like the redirect_to method returns a logically true value. It's much better to do return redirect_to ... so there's no chance that will fail.Haynie
Normally I recommend doing @user.update_attributes! and redirecting. If there's a problem updating you'll get an ActiveRecord::RecordInvalid error which you can rescue and handle with render(action: 'edit').Haynie
I'm not clear on what hte answer is after reading your comments. Execution is clearly passing to the 'render "edit"' line because there's an error in the model, but the URL on the resulting page is not the same as it was before.Pneumodynamics
You're on the edit action, so of course it won't be the same. The only way to fix that is with some URL trickery, like HTML5 history manipulation, or by doing the validation remotely using AJAX before submitting to be sure it's already good to go before you commit and redirect. That's usually a lot more work.Haynie
F
4

I would argue that your assumption is not correct. You ask How do I prevent the URL in the browser bar from changing when a user is returned to a form to correct errors?

And the answer is: The URL in the browser doesn't change when the user sees the form again to correct errors. Because the URL had already changed when the form was sent the first time.

With a routes.rb following common conventions, you would have the following routes (use rails routes to list them):

GET    /users(.:format)          users#index
POST   /users(.:format)          users#create
GET    /users/new(.:format)      users#new
GET    /users/:id/edit(.:format) users#edit
GET    /users/:id(.:format)      users#show
PATCH  /users/:id(.:format)      users#update
PUT    /users/:id(.:format)      users#update
DELETE /users/:id(.:format)      users#destroy

The user update form is rendered, when you do a GET request to /users/:id/edit. But the actual update request from that form is posted to PATCH /users/:id. That means the browser uses already another URL.

Within the update method (remember it is already at the URL /users/:id) you basically have only two options:

  1. Render something. Rendering a view within the action keeps the URL the same (it would still be /users/:id), but you have the change to use the user assigned to @user for example to show its errors on a form. This is Rails' default behavior when an update fails.
  2. You can redirect the browser to another URL (for example back to /users/:id/edit or a totally different URL like the homepage). This changes the URL in the browser's address bar. But a redirect always makes a new GET request, that means you lose the information send via post and you would lose the instance of User currently assigned to @user. After a GET the user is reloaded from the database and therefore has no errors assigned. In Rails, the redirect is used when the update was successful to prevent another POST if the user hits the reload button in the browser.

IMHO, the answer to your question is: You cannot do what you want without leaving the path chosen by Rails and building a completely different route, controller, and form setup. You would need to configure a route that is the same for GET and PATCH (or POST) requests. And the controller method needed to be able to handle both types of requests in different ways. Since Rails 2.0 resource routing is the preferred way. What you want was common before Rails 2.0

My advice is: Follow Rails conventions. The URL is not important, nobody really cares. I see no benefit in not following Rails conventions just to have another URL for a form.

Forename answered 7/1, 2017 at 10:24 Comment(1)
As I'm new to Rails, I'm not fully understanding what the answer is, although I edited my question to include my "rake routes". As for "The URL is not important, nobody really cares", I care and that's why I asked the question. If you feel it is silly or stupid, feel free to flame/downvote me or just don't answer but my question remains.Pneumodynamics
S
2

you can use this syntax:

redirect_to :back
Showers answered 2/1, 2017 at 23:27 Comment(1)
That doesn't work. Although the URL is the same, any error messages that were tied to the model do not get dispalyed in the view as they used to before when I had "render".Pneumodynamics
F
1

I suspect this may be what you're looking for.

redirect_to :edit

http://api.rubyonrails.org/classes/ActionController/Redirecting.html

Firebrand answered 6/1, 2017 at 22:22 Comment(1)
Although I added "redirect_to :edit", it resulted in the error, "undefined method `edit_url' for #<UsersController:0x007fab10d46968> Did you mean? edit_user_url" . Also, are you sure your solution is not going to suffer from the same problem that djothefou's solution did?Pneumodynamics
C
1

I struggled with the same when I started using rails.

However…

One thing to keep is mind why Rails is called "Rails."

Rails is called Rails because the core design principle of Rails is about railroading. Rails makes it easy to build a website by favoring convention over configuration and that means offering one and only one way to do things, hence the railroad analogy.

We as programmers, when we choose to use Rails we agree to stick with the railroad tracks that Rails has created for us in exchange for an easy programming experience. It is just like choosing a train over a car, you have to stay on track but in exchange things get easier.

Hence everyone's answer will most likely be along the lines of "don't bother about going offtrack, just follow the railroad tracks."

You can read more about "Convention over Configuration" and Rails's design philosophy in this article by DHH, who is the creator of Rails — http://rubyonrails.org/doctrine

Chinatown answered 8/1, 2017 at 21:58 Comment(0)
F
0

Even if it is not recommended, it is possible to do what you want :

  • Use render to display the errors
  • Create a route PUT or PATCH with the URL /users/edit in routes.rb
  • Use this route in your view (check form_for parameters)
  • Store the user id somewhere:
    • if you are editing the logged in user, I'd recommend to use the session.
    • otherwise, I'd recommend to add hidden_field_tag parameter in the form
  • Update the current_user function in your controller accordingly
Flintshire answered 9/1, 2017 at 23:30 Comment(0)
P
0

Use this

redirect_to edit_user_path
Pyrophoric answered 10/1, 2017 at 8:5 Comment(1)
Even better, redirect_to edit_user_path(current_user) so Rails will know which userPosy
A
0

Keep the show and edit templates same, which would work for both purposes, it is actually editing page when edited, else treated as show page, when having errors available, it will show errors

Admittedly answered 12/1, 2017 at 12:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.