I have a create action in one of my controllers which responds to ajax and html requests. Inside the create action I set an instance variable (#location) using a find_or_initialize_by_such_and_such. I then test to see if the location already exists or not using this:
if #location.id.nil?
#location.save
respond_with(#location, :location => root_path) do |format|
format.html { redirect_to root_path }
end
end
This works fine since basically the partial (create.js.erb) appends the newly saved location object to a list.
The problem occurs when the user inputs a location which already exists (i.e. has an id). In such a case I do not want the found location to be appended to the list, as it would cause duplication (all the locations are already there). Since #location.id.nil? is false in that scenario the code within the above block is obviously not executed, but I am having a hard time figuring out how to handle that situation. I tried doing something like:
respond_with(nil, :location => root_path) do |format|
format.html { redirect_to root_path }
end
but the js.erb file still grabs the #location instance variable and executes the javascript that adds the object to the list.
What would be the best way to therefore to work around this, so that in cases where the find_or_initialize_by returns an already created object, the response will not execute the javascript to append this object to a list?
remove
respond_with(nil, :location => root_path) do |format|
format.html { redirect_to root_path }
end
and leave only this
redirect_to root_path
if you don't any extra respond
Related
I have several models with has_many :attachments.
I'm trying to redirect back to the Note view after the Note is created.
This is the attachments controller code I'm trying. The #note tells me that this attachment is related to that Note.
# GET /attachments/new
# GET /attachments/new.json
def new
#attachment = Attachment.new
#comment = params[:comment_id]
#note = params[:note_id]
respond_to do |format|
format.html # new.html.erb
format.json { render json: #attachment }
end
end
# POST /attachments
# POST /attachments.json
def create
#attachment = Attachment.new(params[:attachment])
respond_to do |format|
if #attachment.save
if #note != nil
format.html { redirect_to note_path(#note), notice: 'Attachment was successfully created.' }
else
format.html { redirect_to attachments_path, notice: 'Attachment was successfully created.' }
end
But, #note is nil by the time the create code happens.
Thanks for the help!
As a rule, you probably won't see "new" and "create" blocks executed in the same context. That's a bit of a mouthful, so lets be a bit more specific: the variables you declare in "new" won't still exist when "create" is called. So, any variables you want to use in "create" must be declared there as well.
One thing you can do (depending on the code) is share a block between different controller methods that initialized these variables for you. For example:
before_filter :initialize_vars, only: [:new, :create]
...
def initialize_vars
#note = params[:note_id]
end
The "before_filter" will execute the "initialize_vars" method before any new request is sent to the "new" or "create" methods.
More generally, this relates to a pretty important Rails concept (and server-side web engineering in general) - that there is very little "state" within the server. The server takes a request, processes it, and forgets about it. Everything that's needs to be remembered must be stored in the server, or somehow communicated by the request the user sends.
I feel somewhat stupid about this one, but:
if #prof.update_attributes(params[:profile])
respond_to do |format|
format.html {redirect_to(#prof, :notice => "Profile successfully created.") }
end
end
...is in the update method of my controller. I have some attributes being validated in the model.
If validation fails, I just want them back on the same form to be scolded by various red text. (ie. everything in the errors array).
I'm getting a 'template missing' error when validation fails - the template for 'update'. I feel like I'm overlooking something extremely simple. Help!
Try this:
respond_to do |format|
if #prof.update_attributes(params[:profile])
format.html { redirect_to(#prof, :notice => "Profile successfully created.") }
else
format.html { render action: 'edit' }
end
end
The cause of the error is due to the fact that Rails, unless told otherwise, will attempt to render a template with the same name as the action, in this case update, which obviously doesn't exist.
What you want to do is tell rails to render the edit action again in the event of an error. Typically, you would do this with the respond_to block, allowing the block to respond differently depending on whether validation passed or failed.
At present, you have your if statement wrapping the block, and no statements telling rails to render differently in the event of an error. To fix this, I would do the following:
respond_to do |format|
if #prof.update_attributes(params[:profile])
# all is well, redirect as you already wrote
else
format.html { render action: 'edit' }
end
end
I'd like to pass the view controller a params that it can consume after a successful create event has been triggered. Right now at the end of the create event I have the following:
respond_to do |format|
if #link.save
format.html { redirect_to #link, :notice => 'Link was successfully created.', :first => 'true' }
else
format.html { render action: "new" }
end
end
I was trying to pass a 'first' parameter in the 3rd line down, but I'm either not calling it correctly on the view page or I'm not setting it correctly here. Below is the code I'm using to try and call it on the view page:
params[:first]
I think Quatz's answer won't work. Instance variables will not be available after you send redirect_to, which is because redirect_to actually returns 302 back to the browser with a redirect Location header so that the browser will be redirected. So the instance created in previous request won't be available at all.
There could be two solutions to your problem:
Use session. session[:first] = true before you call the redirect_to. After that, you can access the value using session[:first]
Modify your redirect_to to something like
redirect_to link_path(#link, :first => 'true'), :notice => 'Link was successfully created.'}
using the named routes you can pass in parameters.
You can use instance variable in such case. Create an instance variable #first like this.
#first = params[:first]
In your view file use this.
<%= #first.attributes %>
I currently have a create action in my sales controller that looks like this:
def create
#sale = Sale.new(params[:sale].except(:vehicles_attributes))
if #sale.save
redirect_to #sale, :notice => "Successfully created sale."
else
render :action => 'new'
end
end
The intention is to exclude a couple of attributes that are used only to populate linked selects, and should not be submitted (there are no columns for them).
With the controller code above, I am finding that the parameters still includes "sale"=>{"vehicles_attributes"=>{"0"=>{"make"=>"","model"=>""}}} so it seems that I have missed something in the controller code.
EDIT: After some more digging around, I have found that the mass_assignment exception is firing before my except code gets a chance to remove the params that shouldn't be sent by the form, so I am back to square one.
How can I ensure that I remove the fields that shouldn't be sent by the form before I get the mass_assignment error?
As far as I know the mass_assignment error should occur during the new call, so your way should work. Although I never used the except method. Have you tried using the reject! method?
def create
params[:sale].reject! { |k, v| k == :vehicles_attributes }
#sale = Sale.new(params[:sale])
if #sale.save
redirect_to #sale, :notice => "Successfully created sale."
else
render :action => 'new'
end
end
If you need to keep the :vehicles_attributes you can also use the reject method (without the bang) which gives you a copy instead of removing it from the original hash.
Suppose you have an edit form with :remote => true. Your controller method looks like
def update
#article = Article.find(params[:id])
respond_to do |format|
if #article.update_attributes(params[:article])
format.html { redirect_to #article}
format.js { render :js => "window.location.replace('#{article_path(#article)}');"}
else
format.html { render :action => "edit" }
# Render update.js.erb which replaces the body of the form
format.js {}
end
end
end
What's the best way to do the redirect on successful article update in Rails 3.2.1? The raw JS solution seems a little sleazy, but I do like the fact that it's obvious that it's performing the same function as the format.html version.
I would prefer a solution that does not use RJS (if that even works in Rails 3.2?)
How about adding the below line of code on the view file itself
#some_file.js.erb
`window.location = redirect_path
As you mentioned in the question you do not prefer RJS, but I think it's better to follow this pattern better than writing the js code in the controller.
Does your ajax interact with a model (.php,.asp?). My preferred method in this instance is to create a success/fail criteria within the model after submission and redirect directly from there. Not sure if that makes sense in this application though?