Player won't display that it is added - html

I'm trying to add a new player but it won't display and i keep getting a rollback. I've tried using hidden_field for my user_id but it didn't seem to do anything. Are there any other alternatives of doing this? I'm pretty sure its something so small that I have to change. Please help.
Player controller:
def new
#players = Player.new
end
def create
#players = Player.create(user_id: params[:player][:user_id], first_name: params[:player][:first_name], last_name: params[:player][:last_name], position: params[:player][:position], favorite_team: params[:player][:favorite_team], extra_equipment: params[:player][:extra_equipment], bio: params[:player][:bio])
redirect_to players_path
end
player new:
<h1>Add a new Player</h1>
<%= form_for #players do |f| %>
<p>First Name <%= f.text_field :first_name %></p>
<p>Last name <%= f.text_field :last_name %></p>
<p>Position <%= f.text_field :position %></p>
<p>Favorite Team(optional) <%= f.text_field :favorite_team %></p>
<p>Extra Equipment <%= f.text_field :extra_equipment %></p>
<p>Bio <%= f.text_field :bio %></p>
<%= f.hidden_field :user_id %>
<%= submit_tag "Add"%>
<% end %>
Player Model
class Player < ApplicationRecord
belongs_to :user
has_many :pickup, through: :player_pickup
end

For crud (Create, Update, Retrieve & Destroy) use #player, not #players.
Also, call your params from a private method instead calling it directly, this is a DRY concept in rails.
So it would be somehow like this (example):
def new
#player = Player.new
end
def create
#player = Player.new(player_params)
if #player.save
// Do something
else
// Do something
end
end
private
def player_params
params.require(:player).permit(:param_1, :param_2 .... etc)
end
Then in your view use form_for #player

You aren't passing a value into the hidden field. You should be doing something like
<%= f.hidden_field :user_id, value: current_user.id %>
If the user_id is always the current_user - you don't need a field, you can just assign it in the controller.
def create
#players = Player.create(first_name: params[:player][:first_name], last_name: params[:player][:last_name], position: params[:player][:position], favorite_team: params[:player][:favorite_team], extra_equipment: params[:player][:extra_equipment], bio: params[:player][:bio])
#players.user_id = current_user.id
#players.save
redirect_to players_path
end
Or - if you want to keep the same formatting
def create
#players = Player.create(user_id: current_user.id, first_name: params[:player][:first_name], last_name: params[:player][:last_name], position: params[:player][:position], favorite_team: params[:player][:favorite_team], extra_equipment: params[:player][:extra_equipment], bio: params[:player][:bio])
redirect_to players_path
end

Related

How to fix extra data appearing in the ERB generated HTML

I am following ruby-on-rails instruction guide to creating a simple blog web application: https://guides.rubyonrails.org/getting_started.html#generating-a-controller
All my project files are pretty much the same as the ones in the guide.
app/views/articles/show.html.erb
<p>
<strong>Title:</strong>
<%= #article.title %>
</p>
<p>
<strong>Text:</strong>
<%= #article.text %>
</p>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<h2>Comments (<%= #article.comments.count %>)</h2>
<%= render 'comment_section' %>
<%#= render #article.comments %>
<%= link_to 'Edit', edit_article_path(#article) %> |
<%= link_to 'Delete', article_path(#article),
method: :delete,
data: {confirm: 'Are you sure?'} %> |
<%= link_to 'Back', articles_path %>
app/views/comments/_form.html.erb
<%= form_with(model: [#article, #article.comments.build], local: true) do |form| %>
<p>
<%= form.label :commenter %><br>
<%= form.text_field :commenter %>
</p>
<p>
<%= form.label :body %><br>
<%= form.text_area :body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
app/views/articles/_comment_section.html.erb
<% if #article.comments.count > 0 %>
<%= render #article.comments %>
<% else %>
<p>There are no comments yet!</p>
<% end %>
app/views/comments/_comment.html.erb
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Delete comment', [comment.article, comment],
method: :delete,
data: {confirm: 'Are you sure you want to delete this comment?'}
%>
A simple article with no comments works as expected:
However, when showing an article with some actual comments, an extra empty comment gets displayed at the end:
When I try to delete that comment I get the following error (11 in the path is the article_id):
Deleting other comments works fine.
Rest of the files that I think might be relevant:
app/config/routes.rb
Rails.application.routes.draw do
get 'welcome/index'
resources :articles do
resources :comments
end
root 'welcome#index'
end
app/models/article.rb
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
end
app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :article
end
app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
I'm using:
ruby 2.6.5p114
Rails 6.0.0
sqlite3 3.8.7.2
RubyMine 2019.2.3
I'm developing on Windows
The reason why this is happening is this line:
<%= form_with(model: [#article, #article.comments.build], local: true) do |form| %>
The part that says #article.comments.build is building an empty comment on the article. If there are no comments on the article and you were to print out #article.comments.count it would be zero. It does this because #article.comments.count runs a query, and since the blank comment isn't saved yet, it doesn't count it against the comments count.
As a side note, #article.comments.size would return 1, since in this case it returns the size of the relation with the blank comment. This is why you don't get a blank comment when the article has no comments.
However if you were to already have a comment and print out #article.comments.count, it would be 1 because now you have a saved comment in the database. This renders your comments out on the page now. The thing is that there is a blank comment inside of the #article.comments return value. This gets printed out to the screen, and since it doesn't have an id, the route for delete gets rendered like this /article/11/comments without a comment id. This route does not exist, so you get an error.
One possible way to fix this would be to change this line in your comment_section partial from this:
<%= render #article.comments %>
to this:
<%= render #article.comments.select { |comment| comment.persisted? %>
UPDATE:
I think that arieljuod's solution is even cleaner, to change this:
<%= form_with(model: [#article, #article.comments.build], local: true) do |form| %>
To this:
<%= form_with(model: [#article, Comment.new], local: true) do |form| %>
in your views/comments/_comment.html.erb
change
<%= link_to 'Delete comment', [comment.article, comment],
method: :delete,
data: {confirm: 'Are you sure you want to delete this comment?'} %>
to
<%= link_to 'Delete comment', comment_path(comment),
method: :delete,
data: {confirm: 'Are you sure you want to delete this comment?'} %>

Multiple radio buttons getting selected in Rails app

I have an app where Question model has_many relationship with Option. I also have a button to add options while creating a question. Every question has only one correct answer. So when I create a question and click on Add Option button, new option is created but the new radio button associated with it has different name. In fact the name of radio button is of the form question[options_attributes][i][is_answer] where i is id. As far as I know radio buttons should have the same name to work as a collection or group. So how can I make it work as a group even if I create any number of options for a single question?
html.erb
<%= form_for #question do |form| %>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
<%= form.fields_for :options, question.options.each do |a| %>
<div class="field">
<%= a.label :options %>
<%= a.text_area :body %>
<%= a.radio_button :is_answer, "options" %>
<%= a.check_box :_destroy %>
<%= a.label :_destroy, 'delete' %>
</div>
<% end %>
<%= form.submit 'Add option', :name => "add_option" %>
<%= form.submit 'Delete options', :name => "remove_option" %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
controller.rb
class QuestionsController < ApplicationController
def new
#question = Question.new
#question.options.build
end
def create
#question = Question.new(question_params)
#question.user = current_user
if params[:add_option]
#question.options.build
else
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' and return }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
render :action => 'new'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:body, options_attributes: [:id, :body, :question_id, :created_at, :updated_at, :is_answer])
end
end
There are two options:
Using JavaScript on the client-side to uncheck the radio buttons.
Using radio buttons with the same name. It this case you will have to change the way you pass the :is_answer parameter and manually assign the value in options_attributes.
Method 1 details:
See this question radio different names - only check one
Method 2 details:
Instead of passing :is_answer parameter for each option you can pass a single parameter for the question having chosen answer id as the value. Lets name it "answer_id". We want this parameter to be in the params[question]
hash in the controller, so the whole name will be "question[answer_id]". Although radio buttons are generated for each option, only the chosen one will be sent to the server as they all have the same name.
<%= form.fields_for :options, question.options.each do |a| %>
<div class="field">
<%= a.label :options %>
<%= a.text_area :body %>
<%= radio_button_tag "question[answer_id]", a.object.id, a.object.is_answer? %>
<%= a.check_box :_destroy %>
<%= a.label :_destroy, 'delete' %>
</div>
<% end %>
https://apidock.com/rails/v4.2.7/ActionView/Helpers/FormTagHelper/radio_button_tag
In the controller you will have to manually assign the option's is_answer parameter based on the answer_id value.
def question_params
result = params.require(:question).permit(:body, :answer_id, options_attributes: [:id, :body, :question_id])
answer_id = result.delete(:answer_id)
result[:options_attributes].values.each do |option_attrs|
option_attrs[:is_answer] = option_attrs[:question_id] == answer_id
end
result
end
If you need further details please let me know. I will update the answer to give more information.

Incorrect HTML when pre-populating nested forms in Rails 5.1

I have a Rails 5.1 app that consumes the Google Books API and I need to pre-populate nested fields in a form. There are two ways to create a Book.
Firstly, through the normal /books/new form, which accepts_nested_attributes_for :authors with a has_many: through association. Here I am using cocoon gem and everything is working great.
Secondly, a user can create a Book by searching the Google Books API using an ISBN number. This data then pre-populates a form before being submitted to the create action in the BooksController. I have managed to get this working great apart from the ability to correctly submit the nested Author data.
I currently have each author populate a field in the form, yet when I come to submit the data only the last item in the author array (in the case of a book with multiple authors) gets saved.
I believe this is because the form html has the same name and id for both fields as below. How do I get this form to submit both authors?
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
books_controller.rb
class BooksController < ApplicationController
before_action :authenticate_user!
before_action :set_book, except: [:index, :new, :create, :new_book, :submit_book]
def create
#book = current_user.books.create(book_params)
#book.authors.each {|author| author.user_id = current_user.id}
if #book.save
redirect_to book_path(#book)
else
render :new
end
end
def new_book
end
def submit_book
#book = Book.new
#book.authors.new
#response = GoogleBooks.new(params[:q], #book)
end
private
def set_book
#book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :subtitle, :description, author_ids:[], authors_attributes: [:id, :name, :_destroy])
end
end
book.rb
class Book < ApplicationRecord
has_many :book_authors
has_many :authors, through: :book_authors
accepts_nested_attributes_for :authors, allow_destroy: true
validates :title, presence: true
validates_associated :authors
end
google_books.rb
class GoogleBooks
include HTTParty
base_uri 'https://www.googleapis.com/books/v1'
def initialize(isbn, book)
#query = self.class.get("/volumes?q=isbn:#{isbn}")
#book = book
end
def title
#query['items'].first['volumeInfo']['title']
end
def subtitle
#query['items'].first['volumeInfo']['subtitle']
end
def description
#query['items'].first['volumeInfo']['description']
end
def authors
#query['items'].first['volumeInfo']['authors']
#=> ['John J. Ratey', 'Richard Manning']
end
end
submit_book.html.erb
<%= form_for #book do |f| %>
<%= f.text_field :title, value: #response.title %>
<%= f.text_field :subtitle, value: #response.subtitle %>
<%= f.text_field :description, value: #response.description %>
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<% #response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
<%= f.submit %>
Worked it out.
Was a case of changing
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<% #response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
to this
<% #response.authors.each do |author| %>
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
Which produces the following correct HTML:
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][1][name]" id="book_authors_attributes_1_name">

passing params throught rails forms

I am using rails 4 and have a subject and comment models. Subject is a one to many relationship with comments. I want a simple page that can add comments to many subjects on the same page. So in my form I know how to submit a comment to create but I dont know how to find the right subject in my controller to add it to. Any advice?
class CommentsController < ApplicationController
def create
comment = Comment.create(comment_params)
if comment.save
# The line below is incorrect, I dont know what to do
Subject.find(params[:subject_id]).comments << comment
redirect_to(:controller => 'static_pages', action: 'home')
end
end
def new
end
private
def comment_params
params.require(:comment).permit(:text, :user_name)
end
end
StaticPages#home Find me in
app/views/static_pages/home.html.erb
<% #subjects.each do |subject| %>
<div class="subjects <%= cycle('odd', 'even') %>">
<h1><%= subject.name %></h1>
<h3><%= subject.description %></h3>
<% subject.comments.each do |comment|%>
<div class="comment">
<h4><%= comment.user_name%></h4>
<%= comment.text %>
</div>
<% end %>
<%= form_for(#comment) do |f| %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :text %>
<%= f.text_field :text %>
<%= f.submit('Create comment', subject_id: subject.id) %>
<% end %>
</div>
<% end %>
The simplest way would be to populate the subject_id attribute of your #comment form, like this:
<%= form_for(#comment) do |f| %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :text %>
<%= f.text_field :text %>
<%= f.hidden_field :subject_id, value: subject.id %>
<%= f.submit('Create comment', subject_id: subject.id) %>
<% end %>
This will populate the subject_id attribute of your new Comment object, which will essentially associate it through Rails' backend:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def create
#comment = Comment.new comment_params
#comment.save
end
private
def comment_params
params.require(:comment).permit(:subject_id, :text, :user_name)
end
end
--
foreign_keys
This works because of the Rails / relational database foreign_keys structure
Every time you associate two objects with Rails, or another relational database system, you basically have a database column which links the two. This is called a foreign_key, and in your case, every Comment will have the subject_id foreign_key column, associating it with the relevant subject
So you may have many different forms using the same #comment variable - the trick is to populate the foreign_key for each one

Rails: Issue with recieving nested forms with has many through join

I seem to be having a problem with receiving products through my join table, it's giving me a strange error as it seems to be receiving no ID for my order. I can only assume that this is because the order has not been created yet, but I am creating the order during this step anyway, so the order doesn't have an ID yet. So this is my problem.
Here is the error I recieve:
ActiveRecord::RecordNotFound in OrdersController#create
Couldn't find Product with ID=1 for Order with ID=
Rails.root: /BillingSystem
Application Trace | Framework Trace | Full Trace
app/controllers/orders_controller.rb:10:in `new'
app/controllers/orders_controller.rb:10:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"jE2wdERoxE7PKwBhN60KAfguxwAq8qdW4wbru51SMFg=",
"order"=>{"client_id"=>"1",
"products_attributes"=>{"1368396234677"=>{"id"=>"1",
"_destroy"=>"false"}}},
"commit"=>"Create Order"}
Show session dump
Show env dump
Response
Headers:
None
New Order View:
<% if current_user %>
<div id="dashboard">
<div id="logo"></div>
<table id="go_back_link_container">
<tr>
<td>
<div class="go_back_link">
<%= link_to "<- Go Back", "/orders/view" %>
</div>
</td>
<td>
<div id="user_display">
Logged in as <%= current_user.email %>.
<%= link_to "Log out", log_out_path %>
</div>
</td>
</tr>
</table>
<%= form_for #order, method: :post do |f| %>
<% if #order.errors.any? %>
<div class="error_messages">
<% for message in #order.errors.full_messages %>
* <%= message %> <br>
<% end %>
</div>
<% end %>
<p>
<%= f.label 'Select The Client' %><br />
<%= select :order, :client_id, Client.all().collect { |c| [ (c.firstname + " " + c.surname), c.id ] } %>
</p>
<%= f.fields_for :products do |pf| %>
<% #render 'product_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Product", f, :products %>
<p class="button"><%= f.submit %></p>
<% end %>
<% flash.each do |name, msg| %>
<%= content_tag :div, "* " + msg, :id => "flash_#{name}" %><br />
<% end %>
<div id="copyright-notice"><div id="copyright_border">Copyright © Conner McCabe, all rights reserved.</div></div>
</div>
<% else %>
<script type="text/javascript">
window.location="<%= root_url %>"
</script>
<% end %>
Order Model:
class Order < ActiveRecord::Base
has_many :orderedproducts
has_many :products, through: :orderedproducts
has_one :client
attr_accessible :client_id, :order_total, :delivery_date, :products, :products_attributes
accepts_nested_attributes_for :products, :allow_destroy => true
before_save :generate_total
def generate_total
self.order_total = self.products.map(&:product_price).sum
end
end
Orders Controller:
class OrdersController < ApplicationController
def view
#orders = Order.all
end
def new
#order = Order.new
end
def create
#order = Order.new(params[:order])
if #order.save
redirect_to '/orders/view', :notice => "Order Created!"
else
render "new"
end
end
end
Product Fields Partial:
<fieldset>
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
Products Model:
class Product < ActiveRecord::Base
#This line makes these elements accessible outside of the class.
attr_accessible :product_name, :product_price, :product_quantity, :product_supplier
has_many :orderedproducts
has_many :orders, through: :orderedproducts
#These attributes ensure that the data entered for each element is valid and present.
validates_presence_of :product_name
validates_presence_of :product_price
validates_numericality_of :product_price
validates_presence_of :product_quantity
validates_numericality_of :product_quantity
validates_presence_of :product_supplier
end
Application Helper:
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
Ordered Products Model:
class Orderedproduct < ActiveRecord::Base
attr_accessible :order_id, :product_id, :quantity_ordered
belongs_to :order
belongs_to :product
end
I have listed every possible file that could contain an error, I know it's a bit excessive, but it's everything that is to do with it and better I include it than not at all.
I also followed this railscast guide: http://railscasts.com/episodes/196-nested-model-form-revised
To get to where I am, I edited it slightly so that it was suitable for my application.
Thanks in advance.
We had a similar issue on a project, except the relation was singular. The problem is that ActiveRecord is looking for an existing association; something like order.products.find(1). Since order is as new record this doesn't work.
You could create your own products_attributes= method and define the correct behaviour. But I think that you could just use nested attributes for the join model (Orderedproduct) instead of Product.
class Order
accepts_nested_attributes_for :orderedproducts
end
Then adjust the form fields appropriately. In the new form
f.fields_for :products do |pf| becomes f.fields_for :orderedproducts do |pf|
In the fields partial
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %> becomes <%= f.select :product_id, Product.all().collect {|p| [ p.product_name, p.id ] } %>