I have a review page for schools that i link to like this;
<%= school_reviews_path(school_id: #school.id) %>
Generating a link like this;
http://address.com/school_reviews?school_id=1
However, under the reviews is a form that create a new review posting it create action of schools_reviews controller
However, after creation of the review i want to rerender the page with the accompanying parameters i.e school_id=1
I have tried several methods like this but it aint working instead it redirects to http://address.com/school_reviews without the params meanig we do not fetch the right information.
def create
#review = SchoolReview.new(params[:review])
respond_to do |format|
if #review.save
format.html { redirect_to school_reviews_path(:school_id => #review.school_id) }
format.json { head :no_content }
else
format.html { redirect_to school_reviews_path(:school_id => #review.school_id) }
format.json { head :no_content }
end
end
end
Any ideas, will be greatful.
The problem here is that your form, most likely, isn't setting the school id anywhere, so let's make your code be more like what it should be, first setup routes like this:
resources :schools do
resources :school_reviews
end
Your controller name doesn't change, but the implementation will change to be as follows:
class SchoolReviewsController < ApplicationController
before_filter :load_school
def create
#review = #school.school_reviews.build(params[:review])
respond_to do |format|
if #review.save
format.html { redirect_to school_school_reviews_path(#school) }
format.json { head :no_content }
else
format.html { redirect_to school_school_reviews_path(#school) }
format.json { head :no_content }
end
end
end
protected
def load_school
#school = School.find(params[:school_id])
end
end
You will also have to change the form_for call at your view to something like:
<%= form_for( [#school, #review] ) do |f| %>
Your form content here.
<% end %>
Your link will change as well to:
<%= school_school_reviews_path(#school) %>
You might also want to change the SchoolReview model just to Review to remove the school_school_ from the URLs.
And, as usual, read the documentation on routes to understand what's going on.
Related
Hi I am currently working on a web marketplace app for an assignment that allows users to upload items for sale with images attached, and to edit those listings.
Currently i have utilised simple forms for the edit and add product pages and those work fine but when I click update or add product I get the below error:
ActiveSupport::MessageVerifier::InvalidSignature in ProductsController#create
the error pointed out that line 3 of the below code is the problem:
def create
#product = Product.new(product_params)
#product.user_id = current_user.id
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: "Product was successfully created." }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
My product.rb file looks as such:
belongs_to :user, :optional => true
has_one_attached :picture
end
The simple forms work but for reference the form html looks like such:
<%= simple_form_for#product do |f| %>
<h1 class="heading">Edit Product</h1>
<%= render 'form', product: #product %>
<% end %>
Any help I can get would be appreciated.
When you use the respond_to do |format| method, you need to supply the actual formats that the code should respond to. So, your controller probably needs to look something like this:
def create
#product = Product.new(product_params)
#product.user_id = current_user.id
respond_to do |format|
format.html do
if #product.save
...
end
end
end
But I'd also ask: why are you using respond_to here if you only expect to process the result of a web form? It's something you may need to do at some point, but isn't required initially and adds complication.
If that doesn't resolve the issue, we'll probably need to see your strong-params function (product_params), the Product model definition and your view, at least. For example, you may need to structure the line more like this, because your params may well not exactly match the fields in your model.
#product = Product.new(id: product_params[:id], name: product_params[:name])
Rails adds a special authentication code when it creates a form, to help stop bad actors from spamming or hacking your form. If your page is changed after the code is created, it will become out of date and be rejected, but I'd expect a more specific error if that was happening. Fingers crossed!
I’m using Rails 4.2.3. I want to submit a form in a modal dialog, so I have set up my form like so
<%= form_for #my_object, :remote => true do |f| %>
but if the user submits the form successfully, I would like to reload the page that invoked the modal dialog with a notice of “Saved Successfully.” I can’t figure out what I need to put in my “format.js” to make this happen. This is what I have in my controller so far …
def create
#my_object = MyObject.new(my_object_params)
#current_user = User.find(session["user_id"])
#my_object.user = #current_user
respond_to do |format|
if #my_object.save
format.html { redirect_to controller: "users", action: "index", notice: 'Saved successfully.' }
format.js { render action: ‘../users/index’, notice: ‘Saved Successfully’, location: #my_object }
else
format.html { render action: "index" }
format.js { render json: #my_object.errors, status: :unprocessable_entity }
end
end
end
Right now, a successful submission results in a 500 error complaining about missing partials when I try and execute the above. Pretty sure what I have is wrong anyway.
You can do the following:
#app/controllers/redirect.rb
...
format.js { render js: "window.location='#{url.to_s}'" }
...
If you like keeping things separated, just put format.js in your controller and do the javascript redirect in your view (redirect.js.erb)
In both cases, just set flash[:notice] to whatever you need before redirecting.
redirect_to events_path, format: 'js'
For this you will need to have events/index.js.erb in your file structure.
If you are redirecting anyway, you might as well avoid the remote/AJAX call, and just redirect from the create action.
<%= form_for #my_object do |f| %>
and
def create
#my_object = MyObject.new(my_object_params)
...
redirect_to some_path
end
If you have want to redirect it after successfully create/updated and just use .html method. Otherwise just use JS option like in this LINK.
def create
#my_object = MyObject.new(my_object_params.merge(user: User.find(session["user_id"])))
respond_to do |format|
if #my_object.save
format.html { redirect_to controller: "users", action: "index", notice: 'Saved successfully.' }
else
....
end
end
end
That will help you, from your controller
render :js => "window.location = '/jobs/index'"
(I've broken out the 2nd question that originally was part of this post into a separate post)
I am creating a product landing page with Rails in which users can enter their email address to be notified when the product launches. (Yes, there are services/gems etc that could do this for me, but I am new to programming and want to build it myself to learn rails.)
On submit of the form, if there are errors, the app currently redirects to '/invites' I would like to instead display error messages on the same page/URL as the original form? (In my case, the form is located at root while the error messages are displaying at '/invites')
I have read the Rails Guide on Routes and numerous stackoverflow posts on handling form errors nothing I've found seems to answer the question I have.
Update: Based on the reply from #rovermicrover I would like to clarify that, while I'm open to an Ajax solution, I'm fine with a page refresh that displays the error message. (I was not able to get the recommendation by #rovermicrover to function as desired - see my response to that solution below for more details.)
What I did:
Invite model:
class Invite < ActiveRecord::Base
attr_accessible :email
validates :email, :presence => {:message => "Please enter an email address."}
end
My routes file:
SuggestionBoxApp::Application.routes.draw do
root to: 'invites#new'
resources :invites
end
This is what I have in the Invites controller (I've only included the actions I'm referencing: new, create, show - it's basically the default of what Rails might generate):
class InvitesController < ApplicationController
def show
#invite = Invite.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #invite }
end
end
def new
#invite = Invite.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
end
Please let me know if there is any additional info I can provide in helping to answer this question. Thanks!
Make the form 'remote'
form_for #invite, :remote => true
....
Then in the controller
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.js { render :action => 'create_suc'}
else
format.html { render action: "new" }
format.js { render :action => 'create_fail' }
end
end
end
/invites/create_suc.js.erb
$('#errors').remove()
$('#new_invite').prepend("<div class='Thanks'>Thanks for signing up</div>")
$('#new_invite').hide("")
/invites/create_fail.js.erb
$('#new_invite').html('<%= escape_javascript render("form", :invite => #invite) %>');
Forms is a partial with your.... form in it, and also the handling of all errors on #invite.
There is a way to do this without resorting the making the form submit "remote", from a pure Ruby on Rails perspective. However, you can do this only if the browser has enabled cookies.
The idea is to save the form data in the session information in case of an error.
Just remember to delete the session data in case of success.
def new
#invite = Invite.new(session[:invite])
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
session.delete(:invite)
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
session[:invite] = params[:invite]
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
I want to present users with separate pages/dialogs for editing their own information. However, the information is held in a single model (called User). Now I'm trying to find the best approach for handling the update calls from partials. My code currently:
def edit
render :layout=>!request.xhr?
end
def edit_password
render :layout=>!request.xhr?
end
def edit_extra
unless #user.extra
#user.build_extra
#user.extra.value = 2047
end
render :layout=>!request.xhr?
end
def update
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit", :layout=>!request.xhr? }
end
end
end
The thing is, all forms in methods (edit, edit_password and edit_extra) call the update method. However, there are two problems:
If the data parsing isn't validated, user is presented with the "edit" form, which is incorrect.
I want to have a password confirmation on extra data. User shouldn't be able to edit that information unless they supply a correct password.
I would like to make more generalized solution than just duplicating the update -code. The largest problem is rendering correct layout (edit, edit_password) based on the current action.
For now, I solved the problem by creating separate edit_section parameter that will be handled in update.
def update
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => (t :actionsuccesful) }
format.json { head :no_content }
else
action = if params[:edit_section] then "edit_" + params[:edit_section] else "edit" end
format.html { render :action => action, :layout=>!request.xhr? }
end
end
end
And in forms (edit_password, etc)
=form_for(#user, :remote => true) do |f|
= hidden_field_tag :edit_section, "password"
I've got an problem with my update action for a nested resource.
In my app, my orders have many invoices.
Creating a new invoice, I correctly end up with the following url:
/orders/11/invoices/new
And when I edit the invoice, again, it's all correct:
/orders/11/invoices/3/edit
This works fine when the save is a success, however if the validation fails, it routes back to:
/invoices/3
I have the following in my invoices controller:
def update
# #order = Order.find(params[:order_id])
# #invoice = #order.invoices.find(params[:id])
#invoice = Invoice.find(params[:id])
respond_to do |format|
if #invoice.update_attributes(params[:invoice])
format.html { redirect_to(order_invoice_path(#invoice.order, #invoice), :notice => 'Invoice was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #invoice.errors, :status => :unprocessable_entity }
end
end
end
def edit
#invoice = Invoice.find(params[:id])
3.times { #invoice.invoice_items.build }
end
I'm assuming I need to edit the #invoice.errors part but I don't know what to change it to?
Any help appreciated. Jx
When updating failed, you use "render" (comparing with the "redirect_to" in the succeeding path), this brings you to invoice editing path by default. You can use "redirect_to" here to keep the URI path you want, but need remembering to preserve the models' states so your users don't need to fill the entire form all over again.
A detail instruction can be found here: How to make a render :edit call show the /edit in the address bar
Yan
in your form you should add your order, like this:
<%= form_for [#order, #invoice] ... do |f| %>
...
<% end %>
And then uncomment this two lines
# #order = Order.find(params[:order_id])
# #invoice = #order.invoices.find(params[:id])
so your form will send its request to POST /orders/XX/invoices/XX