Render other view without loosing variable values - ruby-on-rails

I have a helpers in Rails app that return some data based on a variable set in controller: like:
def title
base_title = "Unikernel"
if #title.nil? then
base_title
else
"#{base_title} | #{#title}"
end
end
And in controller (usually in each action) I set the value, eg:
#title = "Solutions"
now when I process some result I get from form submission, if something's wrong I say:
render action: "edit"
or
render "new"
When I'm on the rendered page the variables aren't initialized and so on.
What should I do with this problem?

Usually, when something is wrong while updating, you find yourself in the update action. When something goes wrong while creating, your error message gets shown by the create action. Did you set your variable in your create and update action, too?
def new
#title = "New page"
end
def create
#title = "Errors!"
end
When you submit your form and get errors, you will get #title from your create action and if you haven't set it in create, #title will be nil.
When you call render, you are not redirecting to another action, hence you are still in update.

Related

Conditional routing for nested resource in Rails controller #edit action, depending on where request came from

I have a Foo resource that has_many Bars. I'm using nested resources for a limited number of actions, but otherwise prefer to keep my routing for bars shallow. There are two ways to navigate to the edit view for the Bar object - either from the nested path that includes foo, or from the shallower bar path that isn't nested inside foo. For example, a user might click the edit button from the page at /foos/[:foo_id]/bar/[:bar_id]; or from /bars/[:bar_id].
In the first case, I want the controller to redirect the user back to the parent foo page: /foos/[:foo_id] after the record is updated. In the second case, I want it to redirect to the index view for bars: /bars. I believe I need some sort of conditional in the #edit action in the bars controller that will tell Rails where to go after #update executes.
# config/routes.rb
resources :foos do
resources :bars, only: [:new, :edit]
end
resources :bars
# bin/rake routes:
foo_bars POST /foos/:foo_id/bars(.:format) bars#create
new_foo_bar GET /foos/:foo_id/bars/new(.:format) bars#new
edit_foo_bar GET /foos/:foo_id/bars/:id/edit(.:format) bars#edit
bars GET /bars(.:format) bars#index
POST /bars(.:format) bars#create
new_bar GET /bars/new(.:format) bars#new
edit_bar GET /bars/:id/edit(.:format) bars#edit
bar GET /bars/:id(.:format) bars#show
PATCH /bars/:id(.:format) bars#update
PUT /bars/:id(.:format) bars#update
DELETE /bars/:id(.:format) bars#destroy
The controller for bars:
# app/controllers/bar_controller.rb
def edit
#bar = bar.find(params[:id])
#foo = #bar.foo
end
def update
#bar = bar.find(params[:id])
#foo = #bar.foo
respond_to do |format|
if #bar.update_attributes(bar_params)
format.html { redirect_to #foo, notice: "bar successfully updated" }
else
format.html { render action: "edit" }
end
end
end
I'm trying to change the redirect_to #foo line in the #update action so there is conditional logic that switches out #foo for #bars depending on where the #edit action was initiated. I've tried something like the following to test whether params[:foo] is present when the #edit action is called, setting an instance variable for the redirect.
def edit
if params[:foo]
#redirect_page = #foo
else
#redirect_page = #bars
end
#bar = bar.find(params[:id])
#foo = #bar.foo
end
def update
# code omitted...
format.html { redirect_to #redirect_page, notice: "bar successfully updated" }
# code omitted...
end
This doesn't work. Rails states cannot redirect to nil!. I've also tried something using a test based on URI(request.referer).path in the #edit action, without success.
I'm still not entirely clear how the Rails magic happens in the controller. I believe the #edit action is the proper place to define the conditional for the redirect (or through a method called in the #edit action), as that's where the controller will "see" the incoming request and know where it came from. But I can't quite figure out to capture that information, and pass it along to #update. Appreciate any guidance.
In your edit forms, add a hidden_field_tag:
<%= hidden_field_tag "route", request.env['PATH_INFO'] %>
Then in your controller, you can have an if statement and use a redirect_to based on what the params[:route] is.
I figured it out. The params[:route] method using request.env['PATH_INFO] wasn't working for me, because the 'PATH_INFO' variable in the form was providing the path handed off to the bars#update action, instead of the path where the bars#edit action was initiated.
After clicking "Edit" from the parent foo page at /foos/[:id] the params hash is:
>> params
=> {"controller"=>"bars", "action"=>"edit", "foo_id"=>"3786", "id"=>"16"}
There is no value for params[:route] when the form is first accessed - the hidden field is only added to the params hash after clicking "Update" in the edit form:
>> params[:route]
=> "/foos/3786/bars/16/edit"
This could work, but would require building logic to parse the route in order to redirect to /foos/[:foo_id]
It turned out to be simpler to use the Rails flash method to store the path for redirecting back to the source page. I did this by calling a custom method set_redirect_path in the BarsController, and calling it in bars#edit. This sets a value for the source in the flash, which is available in bars#update. Maybe there's a better/more conventional way to achieve this, but this seems to be a clean and simple way to do what I want.
# app/controllers/bars_controller.rb
def edit
set_redirect_path
#bar = bar.find(params[:id])
#foo = #bar.foo
end
def update
#bar = bar.find(params[:id])
#foo = #bar.foo
respond_to do |format|
if #bar.update_attributes(bar_params)
format.html { redirect_to flash[:source], notice: "bar successfully updated" }
format.xml { head :ok }
else
format.html { render action: "edit" }
format.xml { render xml: #bar.errors, status: :unprocessable_entity }
end
end
end
private
def set_redirect_path
flash[:source] = URI(request.referer).path
end
One advantage of this approach is I can now get rid of conditional logic in the shared partial app/views/bars/_list.html.haml that was required to determine whether clicking the "Edit" button should route to edit_foo_bar_path or to edit_bar_path (i.e. the former is chosen if #foo exists). Consequently, I can delete :edit for the nested resource :bars. Since the flash captures the incoming source of the request and stores it for reference in the #update action, all edit requests can use the same edit_bar_path, regardless of where they originate from. After update Rails redirects the user to the point where they initiated the #edit action.

On create error, should I render `new` or redirect to `new`?

Suppose I have something like this:
def new
#user = User.new
end
def create
#user = User.create(params[:user])
if #user.save
flash[:notice] = 'User created'
redirect_to :action => 'list'
else
flash[:error] = 'Some error here!'
render 'new'
end
end
I think the code is clear.
The problem here is, when the #user object is not saved successfully, should I render new (as above) or should redirect to new?
I know if redirect to new the data input by the user is lost, but if I render new, the URL will be /users/create instead of /users/new (which is ugly!).
You are correct in not using redirect. Redirect is loading an entirely new resource.
render however will keep your session data fresh, and depending on how your form is set up, should repopulate whatever data was inputted.
You mention:
I know if redirect to new the data input by the user is lost, but if I render new, the URL will be /users/create instead of /users/new (which is ugly!).
No, this is not true. If you say render 'new', it will go to the url users/new not create. Create as an action only handles POST requests to your controller, and generally never has a view associated with it. It will instead refer to the new action to handle any errors and displaying of forms.
The create action has this in common with the update action which does the same thing by handling only PUT requests, but refers to the edit action to handle the displaying of views.

Model creates blank page?

when I enter information into my form for a new page, it lets you select which author has created the page, this all works fine.
when I go to save the page i get this error
ActiveRecord::AssociationTypeMismatch in PagesController#create
Author(#2162588860) expected, got String(#2151988680)
it also says I have problems in my Pages_controller in the create action and the show action.
def create
#page = Page.new(params[:page])
if #page.save
redirect_to(#page, :notice => 'Page was successfully created.')
else
render :action => "new"
end
end
def show
#page = Page.find(params[:id])
end
I have changed the params :id to author and it saves an empty record with none of the content that was entered in the form. What should I put in the params? or in the create and show action .
thank you
Looks, like you try to set author param to string while you need to set author_id. I can suupose, you used select control in form, builded like this:
f.select(:author, ... )
instead of
f.select(:author_id, ... )

Persistent variables in Rails 3

def edit
#title ="Edit account"
#page_name = "edit"
end
def update
if #wsp.update_attributes(params[:wsp])
# it worked
flash[:success] = "Profile updated."
if (#title == "Location")
redirect_to wsp_location_path
else
redirect_to edit_wsp_path
end
else
#title = "Edit account"
render 'edit'
end
end
The variable #title is empty when in the update method. How can I make the #title persistent so I can read it?
Each controller action is executed in a separate request, so you are losing the values in between.
You probably need to use session, or better yet flash to store the title across requests.
def edit
flash[:title] = #title = "Edit account"
...
end
def update
...
if (flash[:title] == "Location")
redirect_to wsp_location_path
else
redirect_to edit_wsp_path
end
end
In the edit action, you should write the result to a form field, perhaps a hidden field. Then read that data back in from the submitted form in your update action. You really should not preserve any state server side between these 2 requests.
And while you could use the session, I would advise against this. Overuse of the session for passing tiny bits of short lived data between requests is generally bad form.
If the entire purpose of this is to display it on the Edit page, why not put it there (app/views/the_controller/edit.html.erb)? That way it'd be shown on all requests to that page and you're not putting view / presentation code into the controller where it doesn't belong.

Ruby on Rails when create method fails, render loses local variables

Hey guys I have a simple create method with some validations and whenever the create method fails due to validation errors it re-renders the 'new' action.
The problem is in my new action/view I have a local variable that is established in the action and passed to a partial to render some related information to what the user is creating.
Now when my create action fails and I try to re-render the 'new' action I'm getting the always awesome
undefined method `cover' for nil:NilClass
error.
What is the best way to handle re-establishing my action's local variables on a render instead of redirecting to the action again and the user losing the data they input?
For some clarification. Here is some sample code:
#controller.rb
def new
#summary = User.find(params[:user_id])
#page = Page.new
end
def create
#page = Page.new(params[:page])
if #page.save
redirect_to #page
else
render :action => 'new'
end
end
in my new.html.erb file i have something like this
<%= #summary.cover %>
#page form etc...
When you create the object and attempt to save it, the object still holds the values and the validation errors, so pass it on into the render. Usually it is named the same in your create method as it is in your new method, so the template just works.
if #my_object.save
flash[:notice] = "Successfully created."
redirect_to ....
else
render :action => 'new' #assuming new.html.erb uses #my_object
end

Resources