I need to access the same random shuffling across two different loops on the same ERB page.
Context:
I have questions which have five choices of which one is TRUE for choices.is_correct.
My current ERB (below) successfully displays:
1) Grouped by usage
2) All available questions (shuffled)
3) And all five choices (also shuffled)
What I'd like to add is a separate loop (directly after in the same ERB), which gives:
1) Same grouping by usage
2) Same order of available questions (eg, same shuffle order)
3) The correct answer choice (eg, answer "B" = referencing the same shuffling)
Assigning the question or choice shuffling globally would not work, as I need it to be randomized on each page load.
Current ERB (currently shuffles each loop separately)
<%= #exam.name %>
<br />
<% alpha_numbers = ("A".."Z").to_a %>
<% #book_questions_by_usage.each do |usage, question| %>
<h4><%= usage if usage %></h4>
<% question.shuffle.each_with_index do |question, i| %>
<%= i+1 %>: <%= question.name %>
<ol type="A">
<% question.choices.shuffle.each_with_index do |choice, index| %>
<% choice.alpha_order = alpha_numbers[index] %>
<li><%= choice.name %></li>
<% end %><br />
</ol>
<% end %>
<% end %>
<% #book_questions_by_usage.sort.each do |usage, question| %>
<h4><%= usage if usage %></h4>
<% question.shuffle.each_with_index do |question, i| %>
<%= i+1 %>: <%= question.name %>
<ol type="A">
<% question.choices.select { |choice| choice.correct }.shuffle.each_with_index do |choice, index| %>
<% choice.alpha_order = alpha_numbers[index] %>
Correct Answer: <b><%= choice.alpha_order %>. <%= choice.name %></b>
<% end %><br />
</ol>
<% end %>
<% end %>
question.rb
class Question < ApplicationRecord
before_validation :assign_questionable
belongs_to :questionable, polymorphic: true
has_many :choices, :dependent => :destroy
accepts_nested_attributes_for :choices, allow_destroy: true
choice.rb
class Choice < ApplicationRecord
belongs_to :question
I've spent hours looking into possible solutions with no success so far. Any help is much appreciated!
How about you don't shuffle in the view, but in the controller. Save the shuffled array in an instance method and then use it in the view where you need it.
I would like to show a number of things on my homepage, all being pulled from different tables in the database and just wanted to see the best way to do it.
Reading through https://guides.rubyonrails.org/v2.3.11/active_record_querying.html, I was able to get it to show the correct details, but is this the best way to do it? Are there performance issues to come with this method?
<% about = Page.find(1) %>
<h1 class="title"><%= about.title %></h1>
<% unless about.subtitle.blank? %>
<h2 class="subtitle"><%= about.subtitle %></h2>
<% end %>
<p><%= about.body %></p>
And on a similar topic, what is the best way to iterate and display data? This is what I would like to achieve:
<div class="tabs">
<ul>
<% Chapter.all.each do |c| %>
<li><a><%= chapter.title %></a></li>
<% end %>
</ul>
</div>
Any tips greatly appreciated!
To achieve this
<div class="tabs">
<ul>
<% Chapter.all.each do |c| %>
<li><a><%= c.title %></a></li>
<% end %>
</ul>
</div>
There is a few things that must be present:
First step
You need to have a model or data set that represent your data, example you might have a Person.rb file with name and hair color as properties. That can be your model, all it is doing is defining a dataset.
class Person < ApplicationRecord
scope :latest_Person, -> { order(date: :desc) }
validates :name, presence: true, length: { minimum: 5 }
validates :haircolor, presence: true
end
Second step
You need to declare or instantiate that model in your controller class, example you might need to show a list of persons in a particular order. That can be handled using scopes. Example
def persons
# Render all persons
#persons_all = Person.latest_Person
end
Third step
Calling the declared object from the controller class in the view. Basically all you are doing is referencing the declared object. Example
<% if #persons_all.empty? %>
<h2>No persons available</h2>
<% else %>
<% #persons_all.each do |p| %>
<p><%= p.name %></p>
<p><%= p.haircolor %></p>
<% end %>
<% end %>
Mark as answer is it helped, thank you.
I'm new to Rails and I'm doing my first project. Also, English is not my native language so bear with me, please.
The problem I'm having is that I have a form with multiple instances of the same model, the data is being created correctly but when I try to edit it the form is populated in the wrong way.
I'm making an app to check if everything goes according to the rules.
The items to be checked are in a nested association Chapters->Subchapters->Checks
Every time the checks are submitted a CheckRound is created and the information of every check is stored separately in CheckResults.
CheckRounds
has_many :check_results, inverse_of: :check_round, dependent: :destroy
accepts_nested_attributes_for :check_results, reject_if: proc { |att| att['observation'].blank? }
CheckResults
belongs_to :check_round, optional: true, inverse_of: :check_results
belongs_to :check
Chapters
has_many :subchapters
Subchapters
belongs_to: chapter
has_many: checks
Checks
belongs_to :subchapter
has_many :check_results
The form displays all the Chapters and the nested Subchapters and Checks.
Every Check displays its name and has a text_area as an input.
The user can fill none or many Checks.
<%= form_for(#check_round, :url => {:action => 'update', :client_id => #client.id, :project_id => #project.id}) do |f| %>
<% #chapters.each do |chapter| %>
<%= chapter.name %>
<% chapter.subchapters.each do |subchapter| %>
<%= subchapter.name %>
<% subchapter.checks.each do |check| %>
<%= f.fields_for :check_results do |result| %>
<%= check.name %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
The controller is
def edit
#check_round = CheckRound.includes(:check_results).find(params[:id])
#chapters = Chapter.includes(subchapters: :checks).where("segment_id = ?", #project.segment_id).sorted
end
If for example, I submit that check.id = 3 has the observation = "bad" when I go to edit every check has "bad" in its observation regardless of its id.
I want to know how can I show in edit all the checks with a blank observation but the ones that were created.
Thanks in advance for your time!
Ok, From what i see 2 things that needs to fixed.
1st, your f.fields_for :check_results do |result|
needs an extra parameter to specify which check_results it exactly has to modify... somethings like this:
f.fields_for :check_results, #check_round.check_results.where(check_id: check.id) do |result|
in the exact same place so the check variable is specify the right way.
2de, you need to permit your nested parameters in your controller so they can be saved when u submit. Normally you should see a method called check_round_params in your check_round controller.
this one have to like this for everything to work:
def check_round_params
params.require(:check_round_params).permit(
/*your needed params*/,
check_results_attributes: [:id, :check_id, :observation, /*all your nested params*/]
)
end
In short, your update and your create actions work according to those permitted params, so you need define them there. check_results_attributes: is the way that rails understands those params are for nested models.
Here is some documentation you might find interesting:Nested attributes example
Here is the solution i've promised.
Sinds you have already defined that check results with blank observations had to be rejected and there will to much logic involved in your erb for its own sake, i would put it all in an helper method so your erb will be cleaner. Something like this:
#helpers/check_rounds_helper.rb
def edit_or_instantiate_nested_check_results(f, check_round, check, new_check_result)
if check.check_results
f.fields_for :check_results, check_round.check_results.where(check_id: check.id) do |result|
result.hidden_field(:check_id, :value => check.id)
result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the already present check results
# if u want to add a new check result event if the check is populated
f.fields_for :check_results, new_check_result do |new|
new.hidden_field(:check_id, :value => check.id)
new.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the new check result
else #if there is no existing check result nest a form for a new one
f.fields_for :check_results, new_check_result do |new|
new.hidden_field(:check_id, :value => check.id)
new.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the new check result
end #end if statement
end
Then in your view:
<%= form_for(#check_round, :url => {:action => 'update', :client_id => #client.id, :project_id => #project.id}) do |f| %>
<% #chapters.each do |chapter| %>
<%= chapter.name %>
<% chapter.subchapters.each do |subchapter| %>
<%= subchapter.name %>
<% subchapter.checks.each do |check| %>
<%= check.name %>
<% new_check_result = CheckResult.new(check_round_id: #check_round.id, check_id = check.id) %>
<%= edit_or_instantiate_nested_check_results(f, #check_round, check, new_check_result) %>
<% end %>
<% end %>
<% end %>
<% end %>
And that shoud be it ;). Let me know if it did the trick :D!
KR,
I believe it works like you want with this (code with some simplifications):
Check
class Check < ApplicationRecord
belongs_to :subchapter
has_many :check_results
def check_results_for_form check_round_id
results = check_results.where(check_round_id: check_round_id)
results.any? ? results : check_results.build
end
end
CheckRoundsController
def edit
#check_round = CheckRound.find(params[:id])
#chapters = Chapter.includes(subchapters: :checks).all
end
edit.html.erb
<%= form_for(#check_round, :url => {:action => 'update'}) do |f| %>
<ul>
<% #chapters.each do |chapter| %>
<li>
<%= chapter.name %>
chapter
<ul>
<% chapter.subchapters.each do |subchapter| %>
<li>
<%= subchapter.name %>
subchapter
<ul>
<% subchapter.checks.each do |check| %>
<li>
<%= check.name %>
check
<br>
<%= f.fields_for :check_results, check.check_results_for_form(#check_round.id) do |result| %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
</li>
<% end %>
<ul>
<%= f.submit %>
<% end %>
Your problem is that you are repeating the display of the form fields for check_results. Look at line 7 of your view code:
<%= f.fields_for :check_results do |result| %>
This is displaying the fields for each check result on f.object (which is #check_round). However, this code gets repeated for each check in subchapter. That surrounding block gets repeated for each subchapter in chapter, and the block surrounding that gets repeated for each chapter in #chapters.
When the form is submitted, the params for check_results all have the same names, they are not distinguished by chapter, subchapter, or check. As a result, the only value that gets saved for observation is the last one submitted.
I think a solution for your case would be to only show the check_result form fields associated with the current check in the loop. One way to do that is to put a conditional in the loop starting on line 7 of your view code:
<%= f.fields_for :check_results do |result| %>
<% if result.object.check == check %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
<% end %>
<% end %>
You could also just loop through the check_results independently of the loops for checks, subchapters, and chapters, but I'm assuming that you want to keep that order and context for the UI.
Im new to ruby and rails, however I cant figure out why this doesnt work.
I am doing a simple Blog with posts and its comments, everything works fine but I tried to do my own method inside Post model to get the latest comment inside that post.
class Post < ActiveRecord::Base
attr_accessible :content, :title
has_many :comments
def latestComment(id)
post = Post.find(id)
comment = post.comments.last
end
end
and the index.html.erb
<h1>Hello World!</h1>
<h2>Posts</h2>
<% #posts.each do |post| %>
<h3><%= link_to post.title, post %></h3>
<p><%= post.content %></p>
<%= latestComment = post.latestComment(post) %>
<% end %>
<h3>Add new post</h3>
<%= link_to "Add new post", new_post_path %>
This works, it returns some hexa values, so the object exists, however then I now want to get fields from that object like this
<p><%= latestComment.author %></p>
<p><%= latestComment.content %></p>
It fails and the error is
undefined method `author' for nil:NilClass
which is weird and I dont get it why cant I access comments fields..
///comment.rb
class Comment < ActiveRecord::Base
attr_accessible :author, :content, :post_id
belongs_to :post
end
Since you are looping over multiple posts, it's possible that one of them doesn't have any comments, which makes post.comments.last return nil. You can work around this by checking it before trying to render the comment:
class Post < ActiveRecord::Base
def has_comments?
comments.count > 0
end
def last_comment
comments.last
end
end
Then, on the view:
<% #posts.each do |post| %>
<h3><%= link_to post.title, post %></h3>
<p><%= post.content %></p>
<% if post.has_comments? %>
<p><%= post.last_comment.author %></p>
<p><%= post.last_comment.content %></p>
<% end %>
<% end %>
I'm trying to make a report with predefined questions.
I have made questions from the scaffold and filled it.
Now is to assign answer fields per each questions.
[DATA TYPE]
class Report < ActiveRecord::Base
has_many :ansbwer_singles
end
class AnswerSingle < ActiveRecord::Base
belongs_to :report
end
reports/_form.html.erb
<div class="question">
<% QuestionSingle.all.each_with_index do |question, index| %>
<p><%= index+1 %>. <%= question.content %></p>
<p>
<%= f.fields_for :answer_singles do |answer| %>
<%= answer.text_area :content %>
<% end %>
</p>
<% end %>
it shows well but once submit it makes error
1. Question 1
[text area]
2. Question 2
[text area]
[error when submit]
AnswerSingle(#18194030) expected, got Array(#1133380)
I think the reason is using :answer_singles for fields for.
Is there any better code to implement this?