Error with instance variable in Index view - ruby-on-rails

So in my tutors_controller.rb this is my index action
def index
#tutor = Tutor.all
#tutor = #tutor.fees_search(params[:fees_search]) if params[:fees_search].present?
end
and in my index.html.erb this is the view
<div class='container'>
<%= form_tag(tutors_path, method: :get) do %>
<%= label_tag 'fees_search', 'Max Fees' %>
<%= select_tag 'fees_search', options_for_select((10..50).step(10)) %>
<%= submit_tag 'Filter' %>
<% end %>
<% #tutor.each do |tutor| %>
<% unless tutor.admin? %>
<div class='row' id='tutor-listing'>
<div class='col-xs-4'>
<%= image_tag(tutor.profile.avatar.url, :class => "img-rounded" ) if tutor.profile.avatar? %>
</div>
<div class='col-xs-8'>
<h3><%= link_to tutor.full_name, tutor_path(tutor) %></h3>
<% unless tutor.subjects.nil? %>
<% tutor.subjects.each do |subs| %>
<span class='badge'id='tutor-listing-badge'>
<%= link_to subs.name, subject_path(subs) %>
</span>
<% end %>
<% end %>
<% unless current_tutor %>
<%= button_to "Shortlist Tutor", add_to_cart_path(tutor.id), :method => :post %>
<% end %>
</div>
</div>
<% end %>
<% end %>
</div>
So i understand that when the index view first renders, #tutor would simply be Tutor.all so it renders each individual tutor perfectly.
After trying to filter it though, i start receiving errors. The exact error is NoMethodError in Tutors#indexand the highlighted line is <% unless tutor.admin? %>
profile.rb model
class Profile < ActiveRecord::Base
belongs_to :tutor
scope :fees_to, -> (fees_to) { where("fees_to <= ?", "#{fees_to}") }
end
tutor.rb model
class Tutor < ActiveRecord::Base
has_one :profile, dependent: :destroy
def self.fees_search(n)
#profile = Profile.fees_to(n)
if #profile.empty?
return Tutor.none
else
#profile.each do |y|
y.tutor
end
end
end
end
I get that now my #tutor instance variable has obviously changed. But how do i go about resolving this problem? Should i be rendering a partial instead? Obviously my index action in my controller could be "better" also but i'm quite confused now as to what i should be doing.
Would appreciate any advice! Thank you!

#profile.each do |y|
y.tutor
end
Seems to be a problem. All the other outcomes are a Tutor.something scope, whereas this will return the last tutor only. Change each to map to get an array of Tutors instead.

Related

Couldn't find a template file or inline render method for sub-component

Getting this error:
ViewComponent::TemplateError in Welcome#show
Couldn't find a template file or inline render method for FaqComponent::QuestionComponent.
Kind of stuck forever with this issue, not sure how to resolve it. Trying to render a simple component using view_component gem.
# app/components/faq_component.rb
class FaqComponent < ViewComponent::Base
renders_many :questions, "QuestionComponent"
class QuestionComponent < ViewComponent::Base
attr_reader :question
def initialize(question: "")
#question = question
end
end
end
Now, template file looks like:
file location: app/components/faq_component/faq_component.html.erb
<%= content_tag :div, class: "c-faq", data: { controller: "faq" } do %>
<%= content_tag :div, class: "c-card" do %>
<% questions.each do |q| %>
<%= content_tag :div, class: "c-card__section" do %>
<button class="c-faq__question" data-faq-target="question">
<%= q.question %>
<%= icon("arrow-expand", classes: "u-m-0", size: 12) %>
</button>
<div class="c-faq__answer">
<%= q %> <!- IT'S GETTING ERROR HERE ->
</div>
<% end %>
<% end %>
<% end %>
<% end %>
And finally trying to render here:
<div class="l-container u-mb-xxl u-xs-mb-l">
<div class="l-row">
<div class="l-col-xs-26 l-col-md-24 l-col-md-offset-1">
<h3 class="u-mb-s">Common Questions</h3>
<%= render(FaqComponent.new) do |faq| %>
<% faq.question(question: "What is ViewComponent?") do %>
<p>View component is a pain because it's not working or because I'm using it for the very first time!</p>
<% end %>
<% end %>
</div>
</div>
</div>
Any help will highly appreciable! :bow
Looks like you are inheriting class QuestionComponent from the wrong class, try inheriting it from ViewComponent::Base
# app/components/faq_component.rb
class FaqComponent < ViewComponent::Base
renders_many :questions, "QuestionComponent"
class QuestionComponent < ViewComponent::Base
attr_reader :question
def initialize(question: "")
#question = question
end
end
end
Have to define a template or call method for the QuestionComponent nested in the FaqComponent, that's the source of your errors.
That's why it fails at <%= q %>, which is trying to render the QuestionComponent. If you add a app/components/faq_component/question_component.html.erb file that would likely resolve the issue.
Maintainer replied here. Cheers!

Rails, edit action does not populate the form correctly with many instances of the same model

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.

Showing username with association with Rails

I've been researching but cannot seem to find the answer. All I am looking to do is setup an association for users and articles (the most basic setup =P). Anyway, I setup the has_many and belongs_to in the models, added the user_id to the article model. Within my create action, I added a variable to save the current_user and then I am able to have that appear within the show page. Problem is I cannot have the username appear within the index page (in my situation, it is called community). Here are the various codes:
Article.rb (Model):
class Article < ActiveRecord::Base
belongs_to :user
...
User.rb (Model):
class User < ActiveRecord::Base
has_many :articles
...
articles_controller:
def create
#article = Article.create(article_params)
#article.user = current_user
if #article.save
flash.notice = "Article #{#article.title} has created!"
redirect_to community_path
else
flash.notice = "Try Again!"
end
end
Show View:
....
<p><%= #article.user.first_name %> <%= #article.user.last_name %></p>
....
Community(Index) View:
<div class="center_com_col col">
<div class="main_community">
<% #articles.each do |x| %>
<ul>
<li>
<h2><%= link_to article_path(x) do %><%= x.title %><% end %><br>
<p>
<%= Code to go here for user =><br>
Written on: <%= x.created_at.strftime("%B %d, %Y") %>
</p>
</h2>
</li>
<li>
<p><%= truncate simple_format(x.body), length: 250, escape: false %> (<%= link_to "read more", article_path(x) %>)</p>
</li>
</ul>
<% end %>
</div>
Everything seems to work well but I am clueless when it comes to that community (index) page since I am iterating through the articles.
Thank you very much for any help you may have!
EDIT: Here are my show and community controllers:
def community
#articles = Article.all.order("created_at DESC").limit(5)
#users = User.all.order("created_at DESC")
render :layout => 'community_layout'
end
def show
#article = Article.friendly.find(params[:id])
#users = User.all.order("created_at DESC")
render :layout => 'community_layout'
end
Joe
I agree with BroiSatse.
Following some best practices would prevent these errors:
Don't use x as the variable name unless referring to x position!
Always do if #articles.any? before you begin an each if there are no
articles that will cause an error like Nil class Nil!
Before you use art.user have an if art.user exists.
Here Should be the code:
<ul> <!--The UL should not be part of your each loop. -->
<% if #articles.any? %>
<% #articles.each do |art| %>
<li>
<h2><%= link_to article_path(art) do %><%= art.title %><% end %><br>
<p>
<% if art.user %>
<%= art.user.first_name %> <%= art.user.last_name %><br>
<$ end %></h2>
<%= time_ago_in_words(art.created_at) %>
</p>
</li>
<li>
<p>
<%= truncate simple_format(x.body), length: 250, escape: false %> (<%= link_to "read more", article_path(x) %>)
</p>
</li>
<% end %> <!--end refers to #articles.each -->
</ul>
<% end %>
<!--end refers to #articles.any? -->
I believe that some of your articles may not have been created with users authoring them ensure that your
db/seeds.rb:
user = User.create(email: "email#gmail.com", first_name: Faker.first_name, last_name: Faker.last_name)
user.articles.create(title: "ARTICLEs are awesome man!", body: 'Yes in a shocking report people are reading articles")
Also to test specifically do:
rails c
to enter rails console.
articles = Article.all
articles.each { |art| print art.user } => I am guessing you'll get nilClass Nil!

Rails 4 retrieve nested attributes for display in an index.html.erb

Just looking to find out how to retrieve nested images for display on my front page. I have no problems with a standard model but have been unable to find how to bring has_many nested attribute through. All my nested Forms work fine just have neglected the front end.
eg. product has nested product_images. This doesn't look like a clever way of doing it as the last five images uploaded wont necessarily be related to the last five products added.
Could someone please share an example.
cheers
app/controller/home_controller.rb
class HomeController < ApplicationController
def index
#products = Product.last(5)
#product_images = ProductImage.last(5)
end
end
app/views/home/index.html.erb
<% #products.each do |pd| %>
<div><%= pd.product_name %>
<% end %>
<% #product_images.each do |pd| %>
<%= image_tag (pd.product_image(:medium)) %>
<% end %>
</div>
You can try this:
app/controller/home_controller.rb
class HomeController < ApplicationController
def index
#products = Product.last(5)
#product_ids = #products.collect(:id)
#product_images = ProductImage.where(:id => #product_ids).last(5)
end
end
app/views/home/index.html.erb
<% #products.each do |pd| %>
<div><%= pd.product_name %>
<% end %>
<% #product_images.each do |pd| %>
<%= image_tag (pd.product_image(:medium)) %>
<% end %>

Rails: fields_for with index?

Is there a method (or way to pull off similar functionality) to do a fields_for_with_index?
Example:
<% f.fields_for_with_index :questions do |builder, index| %>
<%= render 'some_form', :f => builder, :i => index %>
<% end %>
That partial being rendered needs to know what the current index is in the fields_for loop.
The answer is quite simple as the solution is provided within Rails. You can use f.options params. So, inside your rendered _some_form.html.erb,
Index can be accessed by:
<%= f.options[:child_index] %>
You don't need to do anything else.
Update: It seems that my answer wasn't clear enough...
Original HTML File:
<!-- Main ERB File -->
<% f.fields_for :questions do |builder| %>
<%= render 'some_form', :f => builder %>
<% end %>
Rendered Sub-Form:
<!-- _some_form.html.erb -->
<%= f.options[:child_index] %>
As of Rails 4.0.2, an index is now included in the FormBuilder object:
https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-fields_for
For example:
<%= form_for #person do |person_form| %>
...
<%= person_form.fields_for :projects do |project_fields| %>
Project #<%= project_fields.index %>
...
<% end %>
...
<% end %>
The answer below was posted many years ago, for a modern approach see:
https://stackoverflow.com/a/22640703/105403
This would actually be a better approach, following Rails documentation more closely:
<% #questions.each.with_index do |question,index| %>
<% f.fields_for :questions, question do |fq| %>
# here you have both the 'question' object and the current 'index'
<% end %>
<% end %>
From:
http://railsapi.com/doc/rails-v3.0.4/classes/ActionView/Helpers/FormHelper.html#M006456
It’s also possible to specify the
instance to be used:
<%= form_for #person do |person_form| %>
...
<% #person.projects.each do |project| %>
<% if project.active? %>
<%= person_form.fields_for :projects, project do |project_fields| %>
Name: <%= project_fields.text_field :name %>
<% end %>
<% end %>
<% end %>
<% end %>
For Rails 4+
<%= form_for #person do |person_form| %>
<%= person_form.fields_for :projects do |project_fields| %>
<%= project_fields.index %>
<% end %>
<% end %>
Monkey Patch For Rails 3 Support
To get f.index to work in Rails 3 you need to add an monkey patch to your projects initializers to add this functionality to fields_for
# config/initializers/fields_for_index_patch.rb
module ActionView
module Helpers
class FormBuilder
def index
#options[:index] || #options[:child_index]
end
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
fields_options[:parent_builder] = self
fields_options[:namespace] = options[:namespace]
case record_name
when String, Symbol
if nested_attributes_association?(record_name)
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
end
else
record_object = record_name.is_a?(Array) ? record_name.last : record_name
record_name = ActiveModel::Naming.param_key(record_object)
end
index = if options.has_key?(:index)
options[:index]
elsif defined?(#auto_index)
self.object_name = #object_name.to_s.sub(/\[\]$/,"")
#auto_index
end
record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
fields_options[:child_index] = index
#template.fields_for(record_name, record_object, fields_options, &block)
end
def fields_for_with_nested_attributes(association_name, association, options, block)
name = "#{object_name}[#{association_name}_attributes]"
association = convert_to_model(association)
if association.respond_to?(:persisted?)
association = [association] if #object.send(association_name).is_a?(Array)
elsif !association.respond_to?(:to_ary)
association = #object.send(association_name)
end
if association.respond_to?(:to_ary)
explicit_child_index = options[:child_index]
output = ActiveSupport::SafeBuffer.new
association.each do |child|
options[:child_index] = nested_child_index(name) unless explicit_child_index
output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
end
output
elsif association
fields_for_nested_model(name, association, options, block)
end
end
end
end
end
Checkout Rendering a collection of partials. If your requirement is that a template needs to iterate over an array and render a sub template for each of the elements.
<%= f.fields_for #parent.children do |children_form| %>
<%= render :partial => 'children', :collection => #parent.children,
:locals => { :f => children_form } %>
<% end %>
This will render “_children.erb“ and pass the local variable 'children' to the template for display. An iteration counter will automatically be made available to the template with a name of the form partial_name_counter. In the case of the example above, the template would be fed children_counter.
Hope this helps.
I can't see a decent way to do this through the ways provided by Rails, at least not in -v3.2.14
#Sheharyar Naseer makes reference to the options hash which can be used to solve the problem but not as far as I can see in the way he seems to suggest.
I did this =>
<%= f.fields_for :blog_posts, {:index => 0} do |g| %>
<%= g.label :gallery_sets_id, "Position #{g.options[:index]}" %>
<%= g.select :gallery_sets_id, #posts.collect { |p| [p.title, p.id] } %>
<%# g.options[:index] += 1 %>
<% end %>
or
<%= f.fields_for :blog_posts do |g| %>
<%= g.label :gallery_sets_id, "Position #{g.object_name.match(/(\d+)]/)[1]}" %>
<%= g.select :gallery_sets_id, #posts.collect { |p| [p.title, p.id] } %>
<% end %>
In my case g.object_name returns a string like this "gallery_set[blog_posts_attributes][2]" for the third field rendered so I just match the index in that string and use it.
Actually a cooler (and maybe cleaner?) way to do it is to pass a lambda and call it to increment.
# /controller.rb
index = 0
#incrementer = -> { index += 1}
And the in the view
<%= f.fields_for :blog_posts do |g| %>
<%= g.label :gallery_sets_id, "Position #{#incrementer.call}" %>
<%= g.select :gallery_sets_id, #posts.collect { |p| [p.title, p.id] } %>
<% end %>
Added to fields_for child_index: 0
<%= form_for #person do |person_form| %>
<%= person_form.fields_for :projects, child_index: 0 do |project_fields| %>
<%= project_fields.index %>
<% end %>
<% end %>
I know that this is a bit late but I recently had to do this you can get the index of the fields_for like this
<% f.fields_for :questions do |builder| %>
<%= render 'some_form', :f => builder, :i => builder.options[:child_index] %>
<% end %>
I hope that this helps :)
If you want to have control over the indexes check out the index option
<%= f.fields_for :other_things_attributes, #thing.other_things.build do |ff| %>
<%= ff.select :days, ['Mon', 'Tues', 'Wed'], index: 2 %>
<%= ff.hidden_field :special_attribute, 24, index: "boi" %>
<%= end =>
This will produce
<select name="thing[other_things_attributes][2][days]" id="thing_other_things_attributes_7_days">
<option value="Mon">Mon</option>
<option value="Tues">Tues</option>
<option value="Wed">Wed</option>
</select>
<input type="hidden" value="24" name="thing[other_things_attributes][boi][special_attribute]" id="thing_other_things_attributes_boi_special_attribute">
If the form is submitted, params will include something like
{
"thing" => {
"other_things_attributes" => {
"2" => {
"days" => "Mon"
},
"boi" => {
"special_attribute" => "24"
}
}
}
I had to use the index option to get my multi-dropdowns to work. Good luck.

Resources