rails render form after error in other controller - ruby-on-rails-3

I have two models in the following releationship: Client has_many products, and Product belong_to client. In the client show view I present a form to create new products, that automatically belong to the current client. The show method in the client controller
def show
#client = Client.find(params[:id])
#products = #client.products.paginate(page: params[:page])
#product = #client.products.new
#product.client_id = #client.id
end
and the show view renders a partial
<h1>New Product:</h1>
<%= render 'shared/product_form' %>
That works, products are correctly created.
When an validation error occurs I set a flash in the product create method and redirect_to the client show page. There I loose the data that has been filled in correctly. I tried save the #product instance variable, which has all the data (including the wrong fields) doing
render client_path(client)
from the product controller, but that produces an error
Missing template /clients/17
with the address being
http://localhost:3000/products
Am I calling this wrong? I know that render ususally renders action of the same controller. Can I somehow render Client::show from the product controller? Is there another way to save the data the user has typed in?

If a validation error occurs you should redirect back to the page that generated the validation error. Ie: if the user is at products/new when they submit the form, then your products#create action should end with render :new to present the products form again.
If your products#create action is receiving the form from clients#show, then you do want to render clients#show with validation errors. In that case, all the information that was completed in the form will be available at params[:product], just like it is coming in to products#create.
You might want to take a look at another answer I wrote recently to understand the flow between controllers.
Specifically, the misunderstanding in your case is as follows:
When you have a validation error the record will not save, so you cannot "go back to that data" because your app has not kept it anywhere. The only copy of the data that was submitted is in the request.
If you REDIRECT you are not forwarding a request, you are responding to the initial POST request (which includes all the form information as params[:product]) by making a new request to a different url. This is why you want to use RENDER.
However, if you try to render clients_path(client), what happens is Ruby will first evaluate clients_path(client) to the string clients/(client.id), or in the example you gave, clients/17.
Then render tries to call render 'clients/17', which you don't have a template for. It's looking for a file called clients/17.html.erb. That's why you get the error you're getting.
So, again, to sum up - your products#create action receives the information that was sent from the form as params[:products]. That information is not available outside of this controller action. So, if there is a validation error, instead of creating the product, this controller action should render the same page that the user came from originally (normally products/new) so that they can see the form they just had (with the information filled back into it if you're using a form builder) and also see the error that prevented saving.
I hope that makes sense, feel free to ask follow-up questions.

Yes, you were calling it wrong.
There are three problems:
render should render a template name, say client/new, shared/form etc. The argument could not be a path or variable. The variable is passed by controller to view, nothing to do with render.
You should not use render for a saving fail. Even if you use a template name as #1 mentioned, you'll end up with a wrong URL like products/create with the client page. That's not acceptable.
My suggestion is to always use redirect for saving fail.
Minor problem. In controller, if you've used #product = #client.products.new, the #product object will have every attributes empty but with a valid client id. So you don't need to assign client id again by #product.client_id = #client.id. But this doesn't hurt the result.

Andrew's great answer plus this:
Where to render comments controller in Rails on model validations failure?
makes the solution more clear.
Your specific example: Just watch out that your create action in the ProductsController has all instance variables it needs to render 'clients/show' from it.

Related

Can I use Rails' :notice to a non-rails page (without using AJAX)?

I have a form that I give to my customers to put on THEIR website. The form allows one of THEIR customers to request an appointment through my scheduling software.
My scheduling software has a controller action that creates the customer record and saves the work request to MY customer's environment (inside my software).
When the record is saved, I would like to pop and alert or do a flash[:success]-esque notice to THEIR customer that the requested was successfully submitted.
I DO NOT WANT TO USE AJAX FOR THIS.
Is there a way to put a on MY customer's page and have the Rails redirect_to :back, :notice => "Request submitted successfully" function properly?
I can't give my customer a form.html.erb file or snippet. It needs to be dumb, pure, basic HTML that will work under any webpage. Lot's of restrictions for this.
I understand how I might do this with AJAX - I want to know if there is a way to do it without AJAX.
I did NOT find any solution to using :notice the way that I want, but one work-around I did find was to put a hidden field in my form, manually set the callback URL, and pass that as the redirect like so:
<input type="hidden" name="redirect" value="CALLBACK URL HERE">
In the controller after I submit my data I do:
redirect_to params[:redirect]
Which works a treat because you can put a nice landing page instead of just having a JavaScript alert saying "Success" or whatever

Rails3 How to get an action to do nothing under condition

I have a view with a form. In one case (one value in the form is nil), I want the controller to do nothing.
But the controller wants to "render" something and is looking for a view matching the action of the submit button, but in that case, there is no view, since I want to do absolutely nothing and the page to remain as it is, WITHOUT re-execution of the page, which would be a waste of server power...
How can I translate this "do nothing" in rails?
I can choose not to answer a question but controller can't, he must response once got a request, that's his job :)
An alternative is to use a client side Javascript validation to prevent the form to be submitted when invalid, say blank input.

Some fields get old values in fragment cache in Rails

I have a view to show the details of a resource that is saved in the database. Some of the details belong to the Resource model itself, and some details belongs to associated models.
In the view I use fragment caching for the details.
When the user presses a button on the view, a part of the view is replaced by a form, so the user can edit the details witout loading a new page. When the form is opened, the cache is expired (it actually is, I have checked). When the user submits the form (using :remote => true), the form is hidden, and the original content is reloaded using jQuery and render partial.
So far everything work just fine. The original content is reloaded correctly with the new values, from code inside <% cache ... do %> and <% end %>.
The strange thing is when I reload the page, some of the new values are gone. Instead some old values are shown (those that should have been cached after the last submit). And the thing that is even stranger is that some of the values are updated, even if they are in the same new cache file as the wrong values.
As far as I can see, values in associated models are correct, while values in the Resource model are wrong (old). I have wondered if it had something to do with sql caching, but I don't think that's the case, because I think the sql cache should be emptied when reloading the page.
If I open the form and submit again, the data is updated, and everything is fine. That is the data from the last submit is coming into the cache. If I change the data in the form before submitting, it is still the data from the last submit that is included in the cache-file. So it seems that data submitted for the Resource model is delayed by one submit, even if the other fields are updated correctly.
If I turn off caching in development.rb, everything works as expected. All data are updated every time.
I do, by the way, have the same problem on my server.
Anyone that has a clue?
Not sure if this will help, but in the last couple of days I have implemented a cache-key based fragment cache scheme on my own site with some success.
I implemented a get_cache_key function in all my models
def get_cache_key(prefix=nil)
cache_key = []
cache_key << prefix if prefix
cache_key << self
child_objects.in_sort_order.each do |child_object|
cache_key << child_object.get_cache_key
end
return cache_key.flatten
end
In my views I then used
<% cache(#home_page.get_cache_key('some_name_for_fragment')) do %>
...Render View
<% end %>
The models now produce a cache key that will invalidate the cache if parent model or any of its children are changed.
The full write up is here, on my website.
Rails caching strategy using key-based approach
It seems like the data are cached a bit to soon after they are updated. My solution so far is to drop caching in the view if it is less then two minutes since the resource was updated. The fragment is uncached until someone updates the page a bit later. This is not a very good solution, while each resource (30 resources) on the page must be queried for update time each time the page is viewed.

Url missing action after model failure

I have a profile controller action UPDATE which updates a user's account information. If the model is deemed invalid, I want to render my EDIT action like this:
if #user.valid?
#update
else
render 'edit'
end
The URL for the EDIT action is /my_profile/edit. However, when the render 'edit' code is processed upon failure, even though the correct view is displayed with appropriate errors, the URL that is loaded is /my_profile.
When I'm editing a profile on /my_profile/edit, how can I get the URL /my_profile/edit to be loaded when I call the EDIT action upon model failure?
Here are the current, applicable routes:
get 'my_profile' => 'my_profile#show'
get 'my_profile/edit' => 'my_profile#edit', as: 'edit_my_profile'
put 'my_profile' => 'my_profile#update'
Info: I'm also a beginner!
I thought "render" means, that only the view 'edit' will be rendered.
If you wanted a new request (which changes your also your url and goes in your edit action in the controller) you need an
redirect_to 'edit'
or am I on the wrong way?

Using ajax for rails image preview in form

I've been working on this issue for about two days now. I've posted more task specific questions, got great answers, only to figure out the approach I was taking wouldn't work.
Basically, I want a user to be able to upload images in a form and see a preview of the image they upload before submitting the form. The images and the parent form have a has_many relationship. The images are nested in the parent form using fields_for.
I tried using a client side approach, but ran into cross browser and security issues. My current approach I'm trying is to save the images, reload the div portion of the page, and then assign the parent_id to the image after the partent form is submitted (since I will not have a id for the partent form until it is created).
Is it possible to use ajax to submit the fields_for portion of a form and not the entire parent form? Has anyone attempted to do something similar?
Of course you can submit only the fields of that image by using jQuery to select only the fields that you want to send, serialize them and send them as data of the ajax request.
Be careful, you need to send the authtoken, that's why I have type=hidden in the selector
var data = form.find('[name*=image],[type=hidden]').not('[name*=items]').serialize();
$.ajax({url: this.form.attr('action'), context: this, type: 'POST', data: data
The next step with the image I once implemented as storing the in their own database table, returning the client the id to that who then adds it to the form.