My nested form is not working properly no matter what I try and I searched all the StackExchange's for a solution to this seemingly easy problem. This is where I am right now to get it to work at show up in the view at all.
The form is using the Event controller create action from a non-restful location, hence the global variable (a pages controller, with a specific page, where the form is generated). My ticket model gets generated when the nested form is submitted, and the Event ID gets passed, but it doesn't fill in the "Name" field for the ticket model because it says "Unpermitted Parameters: Ticket." But they're defined as whitelisted in the Events controller! Argh! I'm thinking something is wrong with the form, but nothing I try seems to work.
Any help would be appreciated.
* UPDATED CODE THAT IS NOW WORKING *
Form.html.erb:
<div class="form-inputs">
<%= simple_form_for #event, :html => { :class => 'form-horizontal' } do |f| %>
<div class="row">
<div class="col-xs-6">
<%= f.input :name, class: "control-label" %>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<%= f.simple_fields_for :tickets do |ticket| %>
<%= ticket.input :name %>
<% end %>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
launchpad_path, :class => 'btn btn-default' %>
<% end %>
</div>
</div>
Event_Controller.rb
def new (this is totally skipped and unnecessary)
#event = Event.new
#ticket = #event.tickets.build
end
def create
#event = current_user.events.build(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Your event was created.' }
else
format.html { render :new }
end
end
end
def event_params
params.require(:event).permit(:name, tickets_attributes: [ :name, :id, :event_id, :_destroy ])
end
Pages_Controller.rb (where the form originate
def new
#event = Event.new
#ticket = #event.tickets.build
end
Event.rb
class Event < ActiveRecord::Base
# Database Relationships
has_many :tickets, dependent: :destroy
accepts_nested_attributes_for :tickets, :allow_destroy => true
end
Ticket.rb
class Ticket < ActiveRecord::Base
belongs_to :event
end
Routes.rb
resources :events do
resources :tickets
end
As well as the information from Alejandro (which is correct), you also have f.simple_fields_for #ticket, ... whereas you should have f.simple_fields_for :tickets, ...
If you check your log/development.log for the Processing by EventsController#create the line after will be a Parameters: line, you'll see that the parameters that have been sent through are under a :ticket key instead of a :tickets_attributes key because of the fields_for error.
Fix that, and the permit line and you should be fine.
Update
Hopefully you realized that you also don't need the #ticket = #event.tickets.build(event_params[:ticket_attributes]) line at all once that fields_for is fixed too. The setting of all the associated tickets is all done via the Event object thanks to the accepts_nested_attributes_for helper.
Just, remove from create action this line:
#ticket = #event.tickets.build(event_params[:ticket_attributes])
And, change your event_params:
def event_params
params.require(:event).permit(:name, :main_event_image, tickets_attributes: [:id, :name, :cost, :event_id, :registration_id, :created_at])
end
Te field name must be: tickets_attributes: [ ... (tickets in plural). I think this do the trick.
Edit: I'm agree with #smathy, if no fix to f.simple_fields_for :tickets ... it can't work.
Your new method must look like this:
def new
#new_event = Event.new
#new_event.tickets.build
end
I'm a fan of standards, and I prefer use #event instead of #new_event as in your form (it's part of convention over configuration on rails)
I was stuck at the same problem like crazy and at the end I was able to fix it... Try placing the binding.pry in the first line of create method and print the event_params hash and check if you see ticket_attributes hash inside of it ... That's when it ll throw unpermitted parameter ... And I see event has_many tickets , so I am guessing ticket_attributes needs to be pluralized to be tickets_attributes
Related
I feel like this should be an easy thing to do in Rails, but all of the examples of nested forms in Rails do not take into account the fact that most nested forms also need to pass the current_user when creating new objects through a nested form.
The only way I can get this to work at the moment is by passing a hidden field such as <%= form.hidden_field :user_id, value: current_user.id %>.
For my specific example, I have a model called "Result" that has many "Lessons" and I'd like to create new lessons through the Result form without passing a hidden :user_id.
This seems unsafe because someone could edit that hidden field in the browser and then submit the form thus associating the submission with a different user. The current_user.id seems like the type of thing you don't want to embed in the html as a hidden field.
So how do you create the association between the nested objects and the current_user without putting that hidden field in the form?
FYI, I'm using the GoRails nested form with stimulus style javascript to add and remove lessons from the result form. (Here's the source code for that example.) Here are the relevant parts of my code:
models/result.rb
class Result < ApplicationRecord
belongs_to :user
has_many :lessons, inverse_of: :result
accepts_nested_attributes_for :lessons, reject_if: :all_blank, allow_destroy: true
end
models/lesson.rb
class Lesson < ApplicationRecord
belongs_to :user
belongs_to :result
end
controllers/results_controller.rb
class ResultsController < ApplicationController
before_action :set_result, only: [:show, :edit, :update, :destroy]
def new
#result = Result.new
#result.lessons.new
end
def create
#result = current_user.results.new(result_params)
respond_to do |format|
if #result.save
format.html { redirect_to #result, notice: 'Result was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_result
#result = Result.find(params[:id])
end
def result_params
params.require(:result).permit(:prediction_id, :post_mortem, :correct,
lessons_attributes: [:user_id, :id, :summary, :_destroy])
end
end
controllers/lessons_controller.rb
class LessonsController < ApplicationController
before_action :set_lesson, only: [:show, :edit, :update, :destroy]
# GET /lessons/new
def new
#lesson = Lesson.new
end
def create
#lesson = current_user.lessons.new(lesson_params)
respond_to do |format|
if #lesson.save
format.html { redirect_to #lesson, notice: 'Lesson was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_lesson
#lesson = Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:result_id, :summary)
end
end
views/results/_form.html.erb
<%= form_with(model: result, local: true) do |form| %>
<h3>Lessons</h3>
<div data-controller="nested-form">
<template data-target="nested-form.template">
<%= form.fields_for :lessons, Lesson.new, child_index: 'NEW_RECORD' do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
</template>
<%= form.fields_for :lessons do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
<div class="pt-4" data-target="nested-form.links">
<%= link_to "Add Lesson", "#",
data: { action: "click->nested-form#add_association" } %>
</div>
</div>
<div class="form-submit">
<%= form.submit "Save" %>
</div>
<% end %>
views/results/_lesson_fields.html.erb
<%= content_tag :div, class: "nested-fields", data: { new_record: form.object.new_record? } do %>
# This hidden field seems unsafe!
<%= form.hidden_field :user_id, value: current_user.id %>
<div class="pb-8">
<%= form.text_area :summary %>
<%= link_to "Remove", "#",
data: { action: "click->nested-form#remove_association" } %>
</div>
<%= form.hidden_field :_destroy %>
<% end %>
I'm sure this is a common problem in Rails but I can't find any tutorials online that have the user_id as a part of the nested fields example. Any help is much appreciated!
Personally, since setting the current_user id is something the controller should care about, I would iterate over all the lessons and set the user_id value there.
def create
#result = current_user.results.new(result_params)
#result.lessons.each do |lesson|
lesson.user ||= current_user if lesson.new_record?
end
... the rest ...
Having a hidden field is a security risk, someone could edit it. I also don't like changing the params hash.
I don't think there is a great way to handle this automatically outside of the view. You would either have to inject the value unto the params or possible have a use default on the user association in Lesson that sets it from the Record's user (belongs_to :user, default: -> { result.user }). In these scenarios, I generally move outside of the default Rails flow and use a PORO, Form Object, service object, etc.
build form like this
<%= form.fields_for :lessons, lesson_for_form(current_user.id) do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
remove hidden user_id field you have added
update your result.rb file
class Result < ApplicationRecord
def lesson_for_form(user_id)
collection = lessons.where(user_id: user_id)
collection.any? ? collection : lessons.build(user_id: user_id)
end
end
My goal is to when adding a new product with the new product form, to have an input where one can add a list of emails separated by a space. The list of emails in this string field would be saved as an array of emails in the email_list array attribute of the Product model. This way each product has many emails. (later an email will be sent to these users to fill out questionaire, once a user fills it out there name will be taken off this list and put on completed_email_list array.
I am relatively new to rails, and have a few questions regarding implementing this. I am using postgresql, which from my understanding I do not need to serialize the model for array format because of this. Below is what I have tried so far to implement this. These may show fundamental flaws in my thinking of how everything works.
My first thinking was that I can in my controllers create action first take params[:email].split and save that directly into the email_list attribute (#product.email_list = params[:email].split. It turns out that params[:email] is always nil. Why is this? (this is a basic misunderstanding I have)(I put :email as accepted param).
After spending a long time trying to figure this out, I tried the following which it seems works, but I feel this is probably not the best way to do it (in the code below), which involves creating ANOTHER attribute of string called email, and then splitting it and saving it in the email_list array :
#product.email_list = #product.email.split
What is the best way to actually implement this? someone can clear my thinking on this I would be very grateful.
Cheers
Products.new View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.input :email %>
<%= f.button :submit %>
<%end %>
Products Controller
class ProductsController < ApplicationController
before_action :find_product, only: [:show, :edit, :update, :destroy]
def index
if params[:category].blank?
#products= Product.all.order("created_at DESC")
else
#category_id=Category.find_by(name: params[:category]).id
#products= Product.where(:category_id => #category_id).order("created_at DESC")
end
end
def new
#product=current_user.products.build
#categories= Category.all.map{|c| [c.name, c.id]}
end
def show
end
def edit
#categories= Category.all.map{|c| [c.name, c.id]}
end
def update
#product.category_id = params[:category_id]
if #product.update(product_params)
redirect_to product_path(#product)
else
render 'new'
end
end
def destroy
#product.destroy
redirect_to root_path
end
def create
#product=current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = #product.email.split
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:product).permit(:title, :description, :category_id, :video, :thumbnail,:email, :email_list)
end
def find_product
#product = Product.find(params[:id])
end
end
To solve your original issue
#product.email_list = params[:email].split. It turns out that params[:email] is always nil
:email is a sub key of :product hash, so it should be:
#product.email_list = params[:product][:email].split
Demo:
params = ActionController::Parameters.new(product: { email: "first#email.com last#email.com" })
params[:email] # => nil
params[:product][:email] # => "first#email.com last#email.com"
I'd say that what you have is perfectly fine, except for the additional dance that you're doing in #product.email_list=#product.email.split, which seems weird.
Instead, I'd have an emails param in the form and an #emails= method in the model (rather than email and #email=):
def emails=(val)
self.email_list = val.split
end
Alternatively, you could do that in the controller rather than having the above convenience #emails= method, similar to the way you're handling the category_id:
#product = current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = product_params[:emails].split
Because you need validations on your emails and to make it cleaner I would create an email table, make Product table accept Email attribues and use cocoon gem to have a nice dynamic nested form with multiple emails inputs.
1) models
class Product < ActiveRecord::Base
has_many :emails, dependent: :destroy
accepts_nested_attributes_for :emails, reject_if: :all_blank, allow_destroy: true
end
class Email < ActiveRecord::Base
belong_to :product
validates :address, presence: true
end
2) Controller
class ProductsController < ApplicationController
def new
#product = current_user.products.build
end
def create
#product = current_user.products.build(product_params)
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:project).permit(:title, :description, :category_id, :video, :thumbnail, emails_attributes: [:id, :address, :_destroy])
end
end
3) View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.association :category %>
<div id="emails">
<%= f.simple_fields_for :emails do |email| %>
<%= render 'emails_fields', f: email %>
<div class="links">
<%= link_to_add_association 'add email', f, :emails %>
</div>
<%= end %>
</div>
<%= f.button :submit %>
<% end %>
In your _emails_fields partial:
<div class="nested-fields">
<%= f.input :address %>
<%= link_to_remove_association "Remove email", f %>
</div>
Then setup cocoon's gem and javascript and you'll be good.
Reference: https://github.com/nathanvda/cocoon
I have looked at various answers to similar questions and haven't quite cracked it.
A wine model is defined with has_one :register, :dependent => :destroy and rightly or wrongly I have added accepts_nested_attributes_for :register. A register is defined with belongs_to :wine.
The code within wines_controller.rb for create is:
def new
#wine = Wine.new
#register = Register.new
def create
#wine = Wine.new(wine_params)
#register = #wine.registers.build(register_params)
respond_to do |format|
if #wine.save
#success
else
format.json { render json: #wine.errors, status: :unprocessable_entity }
format.json { render json: #register.errors, status: :unprocessable_entity }
end
end
end
My form for creating a new wine has the following code:
<%= simple_form_for #wine do |f| %>
# various working elements
<div class="field">
<% f.fields_for :register do |r| %>
<%= r.label :short_name %>
<%= r.text_field :short_name %>
<%= r.label :barcode %>
<%= r.text_field :barcode %>
<% end %>
</div>
When this form is called up no fields are created from the f.fields_for command but this block is executed because I can add test buttons within it to prove it is accessed.
If I try to create a wine I get the following error message:
undefined method `registers' for #<Wine:0x007f1204375330> Did you mean? register register= register_id
I believe that using .build is there to ensure data integrity: I don't want to create a wine that does not have a corresponding register. I have tried thinking about it nested attributes but that seems to be considered a bad plan by many. This current approach feels correct but I think I am missing some understanding of syntax at the very least.
At a later date it will be necessary to have other models linked to register that will not be associated to wines. I was considering a similar approach but I am happy to be told to rethink!
If I understand you correctly you have 2 issues:
Firstly fields for register aren't being displayed - this is partly because #wine.register is nil.
You should change your new action to:
def new
#wine = Wine.new
#wine.register = Register.new
In addition because you are using simple_form_for you will need to use simple_fields_for instead of fields_for
Your second issue that results in the exception tells you everything... you are trying to access #wine.registers, and not #wine.register
Change in your create method to:
#register = #wine.register.build(register_params)
This will fix that issue ... however ... all you really need to do is build the #wine object from your params - your params should be configured to permit the right nested attributes - if it is set up correctly the register object will also be built when building the #wine object.
Your model is already set to accept_nested_attributes and thus will also validate and save the register object when calling #wine.save - no need to explicitly save the register object.
You should have something like:
def wine_params
params.require(:wine).permit(
:attribute1, :attribute2,
register_attributes: [:id, :short_name, :barcode])
end
Try this
Wine and Register models
class Wine < ApplicationRecord
has_one :register, inverse_of: :wine, :dependent => :destroy
accepts_nested_attributes_for :register
end
class Register < ApplicationRecord
belongs_to :wine, inverse_of: :register
validates_presence_of :wine
end
Wines Controller
class WinesController < ApplicationController
def new
#wine = Wine.new
#wine.build_register
end
def create
#wine = Wine.new(wine_params)
if #wine.save
redirect_to #wine
else
render :new
end
end
private
def wine_params
params.require(:wine).permit(:name, register_attributes: [:simple_name])
end
end
My wine_params are specific for
rails g model wine name:string
rails g model register name:string wine_id:integer
Lastly wine form should look like this
<%= form_for #wine do |f|%>
<p>
<%= f.label :name%>
<%= f.text_field :name%>
</p>
<%= f.fields_for :register do |r|%>
<p>
<%= r.label :simple_name%>
<%= r.text_field :simple_name%>
</p>
<% end %>
<%= f.submit %>
<% end %>
So you can modify wine_params and form partial for your application specifics
I have a situation where I am getting the error
Unpermitted parameter: incorporation
however I have it listed in the strong params:
def company_params
params.require(:company).permit(:id, :name, :employee_stock_options, :options_pool, :state_corp, :street, :city, :state, :zip, :_destroy,
incorporation_attributes: [:title, :trademark_search, :user_id, :employee_stock_options, :final_submit, :submit, :_destroy],
names_attributes: [:id, :name_string, :suffix, :approved, :snapshot, :company_id, :_destroy],
There's a bit of a catch that might be contributing to the issue:
The controller in question is actually the Incorporation controller. But, as you might notice, we are using it to create a parent model Company, which has_one :incorporation. I realize that this is a bit odd, but I have reasons for wanting my models to be structured this way AND for using the incorporations_controller for doing it.
Accordingly, I have my form structured in the following way:
<%= simple_form_for #company, url: url_for(action: #caction, controller: 'incorporations'), html: {id:"incorporationform"}, remote: false, update: { success: "response", failure: "error"} do |company| %>
<%= company.simple_fields_for #incorporation do |f| %>
<div class="padded-fields">
<div class="form_subsection">
<%= f.input :trademark_search, as: :radio_buttons, label: 'Would you like us to do a trademark search and provide advice regarding any issues we identify in relation to the name you have selected?', input_html: { class: 'form-control radio radio-false' } %>
</div>
</div>
<% end %>
....
<% end %>
Thanks in advance for any insight
Update: My new and create methods are as follows in incorporations_controller
def new
#user=current_user
#company = #user.companies.build
#incorporation = #company.build_incorporation
#action = "new"
#caction = "create"
end
def create
#snapshot="incorporation"
#company = current_user.companies.build(company_params)
#incorporation = #company.build_incorporation
if #company.save
current_user.companies << #company
if params[:final_submit]
redirect_to incorporations_index_path
else
redirect_to edit_incorporation_path(#incorporation), notice: "Successfuly saved incorporation info."
end
else
render 'new', notice: "Something went wrong; form unable to be saved."
# render :nothing => true
end
end
Update 2: In Case it helps, here are the parameters from the log:
"company"=>{"names_attributes"=>{"145\2853672570"=>{"name_string"=>"test19", "suffix"=>"INC", "_destroy"=>"false"}}, "fiscal_year_end_month"=>"", "fiscal_year_end_day"=>"", "street"=>"", "city"=>"", "state"=>"", "zip"\=>"", "issued_common_stock"=>"10,000,000", "employee_stock_options"=>"false", "options_pool"=>"0", "incorporation"=>{"submit"=>"0"}}, "commit"=>"Save"}
I noticed that (unlike other nested attributes) incorporation does not have the _attributes line after it. Might that be of some significance?
Update3: I also seem to be creating an incorporation entry in the incorporations table with the proper ownership assigned. However no other fields are filled out.
You shouldn't have incorporation in your submitted params anyway - it should be incorporation_attributes (as you've already got in your strong params).
--
If you're using fields_for, you should expect [association]_attributes to be passed as a parameter from your form.
Not having it means you've either not got accepts_nested_attributes_for in your parent model, or you have not built your child object:
#app/models/company.rb
class Company < ActiveRecord::Base
has_one :incorporation
accepts_nested_attributes_for :incorporation
end
--
#app/controllers/incorporations_controller.rb
class IncorporationsController < ApplicationController
def new
#company = Company.new
#company.build_incorporation #-> only needed if a new record
end
def create
#company = Company.new company_params
#company.save
end
end
Update
What a strange issue you have - you're passing names_attributes fine and yet incorporation doesn't work.
The one thing I would say, after looking at your params, is that your incorporation is only passing "submit" => "0". I don't see what that is; anyway there are numerous issues with your form:
def new
#company = current_user.companies.new
#company.build_incorporation
...
end
def create
#company = current_user.companies.new company_params
#company.save #-> don't need to "build" in create
end
This will allow you to...
<%= simple_form_for #company, url: url_for(action: #caction, controller: 'incorporations'), html: {id:"incorporationform"}, remote: false, update: { success: "response", failure: "error"} do |company| %>
<%= company.simple_fields_for :incorporation do |f| %>
<%= f.input ...
<% end %>
When using fields_for, you only need to pass the parent object (in your case #company). Building incorporation will automatically populate fields_for without explicitly declaring it.
The error indicates that we need to define this in company model:
accepts_nested_attributes_for :incorporation
attr_accessible :incorporation_attributes
I have been trying to fix an error associated with using the Ancestry gem for comments on my app for Rails 4. I used railscast episode 262 as a guide. However, unlike the episode, my comments model is a nested resource inside another model.Before I go further, I will supply the necessary code for reference. If you like to read the error right away, it is mentioned right after all the code snippets.
The Relevant Models:
class Comment < ActiveRecord::Base
has_ancestry
belongs_to :user
belongs_to :scoreboard
end
class Scoreboard < ActiveRecord::Base
#scoreboard model is like an article page on which users can post comments
belongs_to :user
has_many :teams, dependent: :destroy
has_many :comments, dependent: :destroy
end
Relevant code in the route file:
resources :scoreboards do
resources :comments
resources :teams, only: [:edit, :create, :destroy, :update]
end
The Scoreboards Controller Method for the page on which one can post comments:
def show
#scoreboard = Scoreboard.find_by_id(params[:id])
#team = #scoreboard.teams.build
#comment = #scoreboard.comments.new
end
The Comments Controller:
class CommentsController < ApplicationController
def new
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#comment = #scoreboard.comments.new(:parent_id => params[:parent_id])
end
def create
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#comment = #scoreboard.comments.new comment_params
if #comment.save
redirect_to scoreboard_url(#comment.scoreboard_id)
else
render 'new'
end
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id).merge(user_id: current_user.id)
end
end
I will include the migration for the ancestry gem if any mistakes were made on that :
class AddAncestryToComments < ActiveRecord::Migration
def change
add_column :comments, :ancestry, :string
add_index :comments, :ancestry
end
end
The following code shows the view code:
Scoreboard#show View which is giving me the error in the last line:
<div class= "comment-section">
<%= form_for [#scoreboard, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :body, class: "comment-field" %>
<%= f.hidden_field :parent_id %> #is it needed to include this here? because this form is for new comments not replies
<%= f.submit "Join the discussion...", class: " comment-button btn btn-primary" %>
<% end %>
<%= nested_comments #scoreboard.comments.reject(&:new_record?).arrange(:order => :created_at) %>
</div>
The (comments partial)_comment.html.erb View:
<div class=" comment-div">
<p> Posted by <%= link_to "#{comment.user.name}", comment.user %>
<%= time_ago_in_words(comment.created_at) %> ago
</p>
<div class="comment-body">
<%= comment.body %>
<%= link_to "Reply", new_scoreboard_comment_path(#scoreboard, comment, :parent_id => comment) %>
</div>
</div>
The helper method to render comments:
def nested_comments(comments)
comments.map do |comment, sub_comment| #the comments.map also gives me an error if I choose to render the comments without the .arrange ancestry method
render(comment) + content_tag(:div, nested_comments(sub_comment), class: "nested_messages")
end.join.html_safe
end
The new.html.erb for Comments which one is redirected to for the replies form submission:
<%= form_for [#scoreboard, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :body, class: "comment-field" %>
<%= f.hidden_field :parent_id %>
<%= f.submit "Join the discussion...", class: " comment-button btn btn-primary" %>
<% end %>
Upon creating a scoreboard, I am redirected to the show page, where i get the following error:
undefined method `arrange' for []:Array
Even though the array of comments is empty, I get the same error if it wasnt. I have tried .subtree.arrange but that gives me the same error. Also, the ancestry documentation said that .arrange works on scoped classes only. I don't know what that means. I would appreciate some help on making the page work so the comments show properly ordered with the replies after their parent comments. If this is the wrong approach for threaded comments(replies and all), I would appreciate some guidance on what to research next.
.reject(&:new_record?) this will return an array. The error sounds like arrange is a scope on ActiveRecord. So move the reject to the end and it should work.
#scoreboard.comments.arrange(:order => :created_at).reject(&:new_record?)
In regards your comment nesting, I have implemented this before, and found the Railscasts recommendation of a helper to be extremely weak.
Passing parent_id to a comment
Instead, you're better using a partial which becomes recursive depending on the number of children each comment has:
#app/views/scoreboards/show.html.erb
<%= render #comments %>
#app/views/scoreboards/_comment.html.erb
<%= link_to comment.title, comment_path(comment) %>
<div class="nested">
<%= render comment.children if comment.has_children? %>
</div>