Rails f.error_messages alwais empty - ruby-on-rails

What did I do :)? In rails form has f.error_messages alwais empty. How can I fix/check this?
Thx

The AR#validate method fills the model's error hash with validation errors.
If between instantiating the model and the call f.error_messages you do not call validate (via AR#save or directly) the #errors hash never gets filled and the errors are never shown).
Also make sure you do not redirect ( the validated object gets lost and a new one is created and has no "filled" #errors hash ), but call render :action => ...

Are you looking for error_messages_for :model?
After validation, this function will build a list of error messages for your view.
For example:
# users_controller.rb
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => 'new'
end
end
# view/users/new.html.erb
<%= error_messages_for :user %>
<% form_for #user do |f| %>
...
<% end %>

Related

Rails - Problems Passing Params Through link_to

I'm trying to update the game_started attribute through a link_to. I've also tried using a form_for via hidden_field with no luck.
I also keep getting the below errors
ArgumentError in GamesController#update
When assigning attributes, you must pass a hash as an argument.
Using Rails 5 and Ruby 2.4
Any explanation would be greatly appreciated!
show.html.erb
<% if #game.game_started %>
# some code
<% else %>
<%= link_to "Start The Game", game_path(#game, :game_started => true), :method => :put %>
<% end %>
GamesController
def edit
end
def update
#game = Game.find(params[:id])
if #game.update_attributes (params[:game_started])
redirect_to #game
end
end
def game_params
params.require(:game).permit(:game_type, :deck_1, :deck_2, :user_1, :user_2, :game_started)
end
Change it to
if #game.update_attributes (game_started: params[:game_started])
redirect_to #game
end
In show.html.erb should change to
<%= link_to "Start The Game", game_path(#game, :game => {:game_started => true}), :method => :put %>
In Controller should be
if #game.update_attributes (game_started: params['game']['game_started'])
redirect_to #game
end
The error is telling you that you're passing the wrong arguments into the update_attributes method call. It's expecting a hash like {game_started: params['game_started']}, while you're just giving it the value of params['game_started']. When you just give it a value, it won't know which field in the model to update. So change your code to:
```
if #game.update_attributes(game_started: params[:game_started])
redirect_to #game
end
```

Undefined method 'to_key' error using form_for

I am getting the following error when trying to use form_for in my Rails application:
undefined method `to_key' for #<Table::ActiveRecord_Relation:0x8a09ca8>
My config/routes.rb is:
root 'welcome#index'
post 'foo', as: 'foo', to: 'welcome#index'
The controller is:
class WelcomeController < ApplicationController
def index
#tables = Table.all
end
def test
#tables = Table.all
end
end
And the welcome/index.html.erb view is:
<p>
<%= form_for #tables, :url => foo_path do |t| %>
<%= t.text_area :name %>
<% end %>
</p>
I've tried to do the url workaround that had been suggested in the documentation, but I'm still getting the same error.
Does anyone know what I am doing wrong? I would like to understand this bug a bit more so I can better deal with it.
As per your code, index is returning a collection. However your view tries to define a form for it. This is unlikely going to be succeed.
Form is for an object, not for collections.
Perhaps you can do something like
def new
#table = Table.new
end
and in new.html.erb
<%= form_for #table do |f| %>
...
<% end %>
And if you would like to stick with index.html.erb with a form. Then you have to edit your routes for index action and also in controller it should be for creating a new object.
def index
#table = Table.new
end
Hope it helps!
I see your code have 3 not true things
As RESFUL standard then:
index action always go through with get action so in route file you should define again same that:
root "wellcome#index"
get "foo", to: "wellcome#index", as: :foo
form_for usually use with model object but not collect as you use #tables, if model object not save into database form_for using to create 1 object to database, otherwise form_for using update that object
if you want create form at index action you can follow me:
def index
#tables = Table.all
#table = Table.new
end
index.html.erb file
<%= form_for #table do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
you need create tables_controller to process request from form send to server. you run: rails g controller tables
In table_controller.rb you write same as:
def create
#table = Table.new table_params
if #table.save
redirect_to root_path, notice: "success"
else
redirect_to root_path, alert: "fail"
end
end
private
def table_params
params.require(:table).permit :name
end
so that. end. Have nice day!

How to get a Rails form that is submitted with invalid data to repopulate the user entered fields

I'm working to build a Rails 3 + devise, user registration page. This will be an additional page that does not replace the existing devise registration page. This page will include user info and billing info.
I'm trying to get the form to submit and if the form fields do not save, have the reloaded page include the user's previously inputted data. Here's a snippet:
<%= form_for(User.new, :url => '/pricing/sign_up') do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<% end %>
When the form submits with invalid data. When the view re-renders, the existing email entered is not persisted. How can I make the existing user's input persist to help the user quickly correct mistakes and submit a valid form?
The key is to have the form_for use the right object. So, instead of
<%= form_for(User.new, :url => '/pricing/sign_up') do |f| %>
you should be using an instance variable to contain the object, like this
<%= form_for(#user, :url => '/pricing/sign_up') do |f| %>
The controller actions would look like this:
# Note: this may need to be an `edit` method instead?
def new
#user = User.new
end
# Note: this may need to be an `update` method instead?
def create
#user = User.new(params[:user])
if #user.save
# Do something... Usually a redirect with success message.
else
render :new
end
end
What this create method is doing is it's filling the #user object with params from the form. And then the call to #user.save will, behind the scenes, call #user.valid? and, if no errors are returned, then the record is saved to the database. But this part is key. If #user.valid? does result in errors, then the errors collection on #user will be populated. Then, after the render :new completes, and re-renders your user form, the form will be able to spit out errors messages by accessing the #user.errors collection. Otherwise, the way you had it before, you always had a User.new object in the form which would never have had any errors because it was never used to attempt record validation before.
How to display the errors in your form is a matter of preference and a little beyond the scope of this question. Here's a guide: http://guides.rubyonrails.org/active_record_validations.html#displaying-validation-errors-in-views
I think it's because of your form_for declaration where you're creating new instance of User on every call.
If you move the User.new to your controller and render the new action upon failure in create action then you should see the user entered values in the form fields.
Something like the following should work:
# app/controllers/users_controller.rb
def new
#user = User.new
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
...
else
format.html { render :new }
end
end
end
Then in your view:
<%= form_for(#user, :url => '/pricing/sign_up') do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<% end %>
The values of the form fields are driven by the model you pass to form_for, so in your case they will always be empty because you are passing a brand new user object.
You should be using an instance variable which is set in the controller; in the new action this will be a new User model but in the create action it will be a model which has attributes set via the form:
# app/controllers/users_controller.rb
def new
#user = User.new
end
def create
#user = user.create(user_params)
if #user.save
redirect_to user_path(#user)
else
render :new
end
end
Then in the form:
<%= form_for(#user, :url => '/pricing/sign_up') do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<% end %>

ruby on rails form_for self referential model

Here's a line from my form, used to create a relationship between two users
<%= form_for(current_user.relationships.build(followed_id: #user.id) do |f| %>
<%= f.hidden_field :followed_id %>
<%= f.submit "follow" %>
Relationship controller create method:
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
I'm trying to figure out how to move the current_user.relationships.build logic out of the form and into the controller, is it possible?
I was able to figure out how to move the logic out of the form.
Basically the object being passed to the form_for helper needs to be initialized. I put the initialization logic into user helper and passed the method to the form_for helper. so this is what you get
user helper method
def initializing_relationships
current_user.relationships.build(followed_id: #user.id)
end
in the form you call initializing_relationships
<%= form_for(initializing_relationships) do |f| %>
This really cleans up the form.

Is there a way to do this in Rails without mass assignment?

Members create votes that both belong to them and to another model, Issues. Currently I'm doing this with a hidden form and passing the appropriate parameters. Here's the code on the issues index view:
<%= form_for(#vote) do |f| %>
<%= f.hidden_field "issue_id", :value => issue.id %>
<%= f.hidden_field "member_id", :value => session[:member_id] %>
<%= f.hidden_field "type", :value => :Upvote %>
<%= f.label issue.upvotes_count(issue.id) %>
<%= submit_tag "Up", :class => 'up-vote' %>
<% end %>
This doesn't seem ideal as it leaves issue_id and member_id open to mass assignment. Is there a better way to do this with a button_to tag or something?
Here's the controller code:
class VotesController < ApplicationController
#GET
def new
#vote = Vote.new
end
# POST
def create
#vote = Vote.new(params[:vote])
#vote.member_id = current_member
if #vote.save
redirect_to issues_path
else
redirect_to issues_path, notice: "you must be logged in to vote"
end
end
end
and
class IssuesController < ApplicationController
# GET
def index
#issues = Issue.find(:all)
#vote = Vote.new
end
# GET
def show
#issue = Issue.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
end
Use scope in the controller:
#issue = Issue.find(params[:issue_id])
#vote = #issue.votes.new(params[:vote])
#vote.save
and do not pass member_id and issue_id to hidden fields.
If you have proper nested RESTful routes you should be able to get params[:issue_id] directly.
If issue and member_id are available before you vote.save! in the controller, you can set them manually there.
Normally you get values like member_id from current_user in the controller rather than passing it via form parameters. How you have it currently does expose you to mass-assignment.
Do members have to login before voting? If so, then you don't need to include member_id as a hidden field because you can grab current_user in the controller and this will provide good protection since there wouldn't be any advantage for a member to hack issue_id or type.

Resources