I have 2 models - Project and Review. Project has_many reviews and Review belong_to Project. When I open project (show REST action) I can add review for it. But the controller which accept data from form and save it is different on project(show action in ProjectsController, create action of Review in ReviewsController).
Problem is that I don't understand how render errors of Review model. If I have 1 controller I do something like this:
if #project.save
respond_to do |format|
format.js
format.html {redirect_to :back, :notice => I18n.t("activerecord.attributes.project.added")}
end
else
render :action => :show
end
And rails render error messages under each form field, that has errors. Flash messages don't accepted.
in your reviews controller, try the following
def create
#project = Project.find... # fetch the project
#review = #project.reviews.build params[:review]
#review.save
render template: 'projects/show'
end
in the project page, check for #review and if it has errors
# app/views/projects/show.html.erb
<% if #review && #review.errors.any? %>
...
<% end %>
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!
The problem in brief: I'm working on a rails 4 app (4.1.8) and I'm trying to get flash[:notice] and flash[:alert] to show up under a form.
Two controllers: landingpage_controller and contacts_controller. The landingpage_controller serves a static landingpage through its show action and the contacts_controller has new and create actions, to store the contacts in a db table.
On the static landingpage, a modal with id="contact-modal" contains a partial with a simple_form_for #contact (see below). Upon submittal of the form, a db-entry is not created if the fields are not all filled out and a db-entry is created if the fields are filled out. However, no flash messages are displayed.
Wanted output:
Ideally the partial would re-load without leaving/closing the modal, with either: a success message and an empty form or a alert message and the form as it was upon submittal. How do I do this?
The controller: app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
def new
#contact = Contact.new
render layout: "contact"
end
def create
#contact = Contact.new
respond_to do |format|
if #contact.save
flash[:notice] = "Success"
format.js
else
flash[:alert] = "Error"
format.js
end
end
end
private
def contact_params
params.require(:contact).permit(:email, :structure, :message_content)
end
end
The form: app/views/contacts/_new.html.haml
= simple_form_for #contact, html: { id: "contact-form"} do |c|
= c.input :email
= c.input :structure
= c.input :message_content
= c.button :submit
.messages-container
= if flash[:notice]
%p
= flash[:notice]
= if flash[:alert]
%p
= flash[:alert]
Routes:
resources :contacts, only: [:new, :create]
I'm aware that a partial reload probably involves AJAX. I've read several StackOverflow questions on this but have not been able to figure it out. See here, here and these two blog-posts: jetthoughts, ericlondon.
Your help is very much appreciated
There are several problems in your code:
views, that start with underscore are called partials and are not full actions, but just parts of reusable view code (you don't redirect to them, instead you use render since you usually don't want a full page reload.
1.1 Rename your _new.html.haml to _form.html.haml
2.1 Create a new view new.html.erb(I guess you have that already, otherwise your new action might not work properly) with content = render 'form'
From what I understand you don't want the modal to close, just to render a form after successful submission or if there is an error.
In that case:
1.create a create.js.erb file in your views/contacts folder
create.js.erb
$("#your_modal_id").html("<%= j( render 'form') %>")
2. change your create action
def create
#contact = Contact.new(contact_params)
respond_to do |format|
if #contact.save
flash[:notice] = "Success"
format.js
else
flash[:alert] = "Error"
format.js
end
end
end
to your form add remote: true
WARNING: This will leave your form filled in even if it is successful.
More about this topic see:
http://guides.rubyonrails.org/v4.1.8/working_with_javascript_in_rails.html#form-for
Hope it helps, and I hope I didn't forget anything
In my Ruby on Rails application, each group has_many :expenses. I have nested my routes, so expenses are entered only as child entities of their parent groups. Here's an excerpt from routes.rb.
resources :groups do
resources :expenses
end
I cannot figure out how to render the 'new' action in the case of an expense not saving when it is submitted through /groups/:group_id/expenses/new. In my expenses_controller.rb, here is how the create action is defined:
def create
#expense = Expense.new(params[:expense])
#expense.group_id = params[:group_id]
if #expense.save
redirect_to group_expense_path(#expense.group.id, #expense.id)
else
render 'new'
end
end
Everything works fine if I satisty expense validation and #expense.save winds up working. However, when it fails and the code tries to render 'new' I get:
undefined method `expenses_path' for #<#<Class:0x007fd408b1fd58>:0x007fd408f21ca8>
So, I am assuming I have something about my nested routing wrong. How do I return the user to the new form but still display to him/her through the flash[] params the errors with the data they originally attempted to submit?
The problem is that #group is not initialized
So in your controller just do
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Looks like you need to explicitly specify the url for form_for in your view.
Something likeā¦
<%= form_for #expense, :url => group_expenses_path(#group.id) do |f| %>
...
<% end %>
In your <%= form_for %> you have used #group for url, because expenses belongs_to groups. But inside your create action in the controller you have not defined what is #group, so first you should define it as:
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Also I would suggest to use respond_to in your controller:
respond_to do |format|
if #expense.save
format.html { redirect_to group_expense_path(#group.id, #expense.id), :notice => "Any msg you want" }
else
format.html { render :action => "new" }
end
end
All of these are in your create action inside the controller.
Also for different rendering methods look up: http://guides.rubyonrails.org/layouts_and_rendering.html
Hope this helps!
Since, I am new to rails, so I have want to know a small functionality.
I have a reports model in my rails 3 application(not by scaffolding). I am displaying reports one by one through ajax functionality. I want to add a delete link to my each report. I have also created the destroy method in my controller. Now, I don't know how to delete a specific report when I click on the delete link of that particular report.
Here's my controller code:-
class ReportsController < ApplicationController
def index
#reports = Report.all(:order => "created_at DESC")
respond_to do |format|
format.html
end
end
def create
#report = Report.create(:description => params[:description])
respond_to do |format|
if #report.save
format.html { redirect_to reports_path }
format.js
else
flash[:notice] = "Report failed to save."
format.html { redirect_to reports_path }
end
end
end
def destroy
#report = Report.find(params[:id])
if #report.destroy
format.html { redirect_to reports_path }
format.js
end
end
end
You can assume that my reports are being displayed in the twitter-timeline format and I want to add the delete report feature to each report. Please help me out.
In your view you'd add a link, button, etc. to send the delete action back to the server.
Using link_to for example:
link_to("Destroy", report_path(report), :method => :delete, :confirm => "Are you sure?")
You can do the same with button_to.
Update:
Sorry I missed the AJAX mention (thanks Jeffrey W.).
You'll also want to add :remote => true if you want to send the delete via AJAX.
I have been trying to get to grips with jQuery and been following a railscast on adding an Ajax add review form, which works fine but I would now like to add into it the ability for a review to belong to a user as well as a venue.
Reviews controller
def create
#review = Review.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
views\reviews\create.js.erb
$("#new_review").before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
$("#reviews_count").html("<%= pluralize(#review.venue.reviews.count, 'Review') %>");
$("#reviews").append("<%= escape_javascript(render(:partial => #review)) %>");
$("#new_review")[0].reset();
I have tried changing the controller to:
def create
#review = #current_user.reviews.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
but it just wont submit, with no errors.
I think I have the models set correctly with belongs_to and has_many, I think this is a controller issue I'll add other code bits if needed.
Development log
NoMethodError (undefined method `reviews' for nil:NilClass):
app/controllers/reviews_controller.rb:14:in `create'
Thanks for any help!
It appears that your error is residing with #current_user. According to your development log, #current_user is nil when you call #current_user.reviews on it. I would say track down where this #current_user instance variable is being set and find out why it is nil. Now, what kind of authentication are you using? Most authentication plugins, especially those used by Ryan Bates of the Railscasts you mentioned, use a local variable, say just current_user, as the means to access the currently signed in user. I know I do in all my code.
So, rewrite the line as
#review = current_user.reviews.create!(params[:review])
and see if that works. If it doesn't, change it back and then track down where this #current_user is being set. Chances are good it is being set in a before_filter :method_name at the beginning of your controller.
Calling create! (with exclamation mark) will throw an exception and thus abort your create action if saving fails. Check your log/development.log for these exceptions.
Use build instead of create and lose the exclamation mark.
def create
#review = #current_user.reviews.build(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end