Odd NoMethodError on validation fail - ruby-on-rails

I have a strange NoMethodError that's appearing when a form fails validation in Rails. I can access the 'new' method without a problem to fill out the form the first time. But then the form submits to a nested controller and if validation fails, it tried to redirect to 'new' again (as it should) but throws undefined method articles_path.
I thought it might be to do with the nesting, but I believe the setup here is correct for it to load the nested 'new' path?
I'm sure I'm missing something obvious... But why is this happening?
Controller:
def new
#user = User.find(current_user)
#article = Article.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #article }
end
end
def create
#article = current_user.articles.build(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to root_path, :flash => { :notice =>
"Article added!" } }
else
format.html { render :action => "new" }
format.xml { render :xml => #article.errors, :status =>
:unprocessable_entity }
end
end
end
Form:
= form_for([#user, #article], :html => { :multipart => true }) do |f|
- if #article.errors.any?
#errorExplanation
%h2
= pluralize(#article.errors.count, "error")
prohibited this post from being saved:
%ul
- #article.errors.full_messages.each do |msg|
%li= msg
.clearfix
= f.label :title, "Article title *"
.input
= f.text_field :title

You're right that the error is caused by nested resource, particularly this part of the code
= form_for([#user, #article], :html => ...
When validation fails, #user is nil which causes the url to look like [nil, #article] which turns into articles_path if #article is not persisted. So the solution is to set #user in the create action.

Related

Correct way to pass local variables from a controller to a js.erb which renders a partial

I'm trying to implement an edit function for comments using ajax. The function must load a _form.html.erb partial to edit the comment on which the user clicks but I am not able to figure out how to implement the variables used.
def edit
#comment = Comment.find(params[:id])
#post = Post.find_by_id(params[:post_id])
respond_to do |format|
format.html {}
format.json { head :no_content }
format.js { render "edit", :locals => {:#comment => Comment.find(params[:id]), :#post => #post} }
end
end
def update
#comment = Comment.find(params[:id])
#post = Post.find_by_id(params[:post_id])
if #comment.update_attributes(comment_params)
respond_to do |format|
format.html {
flash[:notice] = "Successfully updated comment"
redirect_to post_path(#comment)
}
format.json { head :no_content }
format.js
end
else
respond_to do |format|
format.html {flash[:alert] = "Error updating coment"
render :edit
flash[:alert] = "Error updating coment"
render :edit}
format.json { head :no_content }
format.js
end
end
end
Here are my js files, edit and update:
$('.edit_comment').bind('ajax:success', function() {
$(this).closest('.comment').html("<%= escape_javascript(render partial: 'comments/form', :locals => {:post => #post,:comment => #comment}) %>");});
$('.edit_comment').bind('ajax:success', function() {
$(this).closest('.comment').replaceWith('<%=j render 'comments/comment', post: #post, comment: #comment%>');
});
There must be some problem with the variables I'm using in my controller and in my js files because when I try and edit more than one comment without refreshing I am always editing the first one I clicked on.
In my console the right comment ids are given when I click on edit, however when the form is loaded on the actual page it is using always the first comment that I tried to edit.
EDIT
adding my form partial:
<%= form_for [post, comment], remote: true do |f| %>
<%= f.text_area :body, placeholder: "Add a Comment" %><br/>
<%= f.submit "Post Comment" %>
<% end %>

rails passing params to view after redirect

I want to pass 2 strings to the view after redirecting.
the controller:
def create
#rating = Rating.new(params[:rating])
respond_to do |format|
if #rating.save
format.html { redirect_to #rating, :notice => 'Got It!' ,
:notice_small => 'Your photo has been uploaded. good luck with it\'s coolness rating!' }
format.json { render :json => #rating, :status => :created, :location => #rating }
else
format.html { render :action => "new" }
format.json { render :json => #rating.errors, :status => :unprocessable_entity }
end
end
end
the view:
<p id="notice" class="big_notice"><%= notice %></p>
<% if defined? notice_small %>
<p id="small_notice" class="small_notice"><%= notice_small %></p>
<% end %>
the notice string goes throw but the notice_small does not, why?
Only :notice and :alert are allowed to be set using redirect_to.
If you want something beyond this, use :flash => { :notice_small => '....' } option for redirect_to or set flash[:notice_small] before redirect_to explicitly.
The redirect looks like it should work as far as I can tell. However, to make it available in your view, the action you're redirecting to would have to take params["notice_small"] and put it in an instance variable. Something like
#notice_small = params["notice_small"]
in the actions, then you could do
<% if defined? #notice_small %>
<p id="small_notice" class="small_notice"><%= #notice_small %></p>
<% end %>

Rails: ActiveRecord::RecordNotFound in AlbumsController#create Couldn't find User without an ID

Hi I'm currently receiving an error in my controller on submitting a form for creating an album. This is my first project and I am pretty shaky on controllers and what params to put and what to set the instance variables as... please help!!
on submission of my new.html.erb form, I receive
ActiveRecord::RecordNotFound in AlbumsController#create
Couldn't find User without an ID
here is my albums_controller
class AlbumsController < ApplicationController
def index
#albums = Albums.all
respond_to do |format|
format.html
format.json { render json: #albums }
end
end
def show
#albums = Album.all
#album = Album.find(params[:id])
#photo = Photo.new
end
def update
end
def edit
end
def create
#user = User.find(params[:user_id])
#user.album = Album.new(params[:album])
respond_to do |format|
if #user.album.save
format.html { redirect_to #user, notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album}
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
def new
#user = User.find(params[:user_id])
#album = Album.new
end
def destroy
end
end
here is the form I'm submitting
<%= form_for (#album), :html => { :id => "uploadform", :multipart => true } do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
Let me know if you need any more files.
In your createmethod you're trying to fetch a user from the database, based on params[:user_id], and params doesn't contain any user_id.
In this case I believe that it should come from the URL
So one solution if an album belongs to the user would be to set your routes like that :
resources :users do
resources :albums
end
Then you'll have to tell your form that the album is nested under a user byt explicitly giving the url. user_albums_path matches /users/:user_id/albums(.:format)
<%= form_for (#album), url: user_albums_path :html => { :id => "uploadform", :multipart => true } do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
The your create method you receive the user_id it need in the params.
Let me know if'm not clear enough or if you need more explanations
You should use the build method defined here
#user = User.find(params[:user_id])
#user.album = Album.new(params[:album])
to create the album, you should
#user = User.find(params[:user_id])
#user.albums.build params[:album])
this will automatically set the user_id attribute of the album to #user.id, you won't have anything to do
so your create method should be
def create
#user = User.find(params[:user_id])
#album = #user.albums.build params[:album]
respond_to do |format|
if #album.save
format.html { redirect_to user_path(#user), notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album} # I don't know what this location is
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end

Rails: form with :url specified can't be used as partial?

Hi I'm trying to use a form as a partial for both the new/edit views of my albums controller/model. However, it's giving me the error when I try to edit the album:
No route matches [PUT] "/users/22/albums"
I think this might have to do with my form's :url. My form works fine when I submit it to create an album, but gets that error when I try to edit it.
I tried taking out the url: user_albums_path in my form, but it would just give me an error when I try to create a new album.
No route matches [POST] "/albums"
Is there any way to make it so that the form works for both actions? I feel like the :url can't coexist properly in both actions.
_form.html.erb
<%= form_for (#album), url: user_albums_path, :html => { :id => "uploadform", :multipart => true } do |f| %>
<div class="formholder">
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
</div>
<% end %>
albums controller
class AlbumsController < ApplicationController
def index
#user = User.find(params[:user_id])
#albums = #user.albums.all
respond_to do |format|
format.html
format.json { render json: #albums }
end
end
def show
#user = User.find(params[:user_id])
#album = #user.albums.find(params[:id])
end
def update
#user = User.find(params[:user_id])
#album = #user.albums.find(params[:id])
respond_to do |format|
if #album.update_attributes(params[:album])
format.html { redirect_to user_album_path(#user, #album), notice: 'Album successfully updated' }
else
format.html { render 'edit' }
end
end
end
def edit
#user = User.find(params[:user_id])
#album = #user.albums.find(params[:id])
end
def create
#user = User.find(params[:user_id])
#album = #user.albums.build(params[:album])
respond_to do |format|
if #user.save
format.html { redirect_to user_album_path(#user, #album), notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album}
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
def new
#user = User.find(params[:user_id])
#album = Album.new
end
def destroy
end
end
please help!
update:
FIXED IT! on my own! the form just needed to be <%= form_for([#user, #album])...
Try
<%= form_for [#user, #album] %>
# other arguments can be inserted before the closing brace
That syntax properly scopes the resource routes
Remember to have an #album instance variable if you need one. You can build an empty album using #user.album.build

Ruby on Rails - f.error_messages not showing up

I've read many posts about this issue but I never got this to work.
My model looks like this:
class Announcement < ActiveRecord::Base
validates_presence_of :title, :description
end
My controller's create method(only its relevant part) looks like this:
def create
respond_to do |format|
if #announcement.save
flash[:notice] = 'Announcement was successfully created.'
format.html { redirect_to(#announcement) }
format.xml { render :xml => #announcement, :status => :created, :location => #announcement }
else
#announcement = Announcement.new
#provinces = Province.all
#types = AnnouncementType.all
#categories = Tag.find_by_sql 'select * from tags where parent_id=0 order by name asc'
#subcategories= ''
format.html { render :action => "new" } #new_announcement_path
format.xml { render :xml => #announcement.errors, :status => :unprocessable_entity }
end
end
end
My form looks like this:
<% form_for(#announcement) do |f| %>
<%= error_messages_for 'announcement' %> <!--I've also treid f.error_messages-->
...
What am I doing wrong?
You are killing your error messages by creating a new announcement in your else statement.
#announcement = Announcement.new # should be removed
When you call #announcement.save it will store the errors in #announcement.errors. By calling #announcement = Announcement.new after this you are going back to a clean slate. So no errors will ever be displayed.

Resources