Create and show items in the samve view - ruby-on-rails

While trying to build a form to create new items on an index page rather than the new view, i ran into a curious problem.
When presenting the form for creation of a new task in the current project, the #tasks variable that contains the tasks that are supposed to be displayed in the list below, seems to be empty. Therefore i get a "No route matches" since the object im passing to ie. complete_project_task_path is nil.
When removing the form, everything works like a charm. Any ideas why this happens?
<h1>Listing tasks</h1>
<%= form_for([#project, #project.tasks.build]) do |f| %>
<div id="new_quick_task">
<%= f.text_field :title, :value => 'Quickly add a new task' %>
<%= f.submit %>
</div>
<% end %>
<div id="task_list">
<% #tasks.each do |task| %>
<div class="task">
<div class="completed"><%= task.completed %></div>
<div class="complete_link"><%= link_to "Good", complete_project_task_path(#project, task) %></div>
<div class="title"><%= link_to task.title, project_task_path(#project, task) %></div>
</div>
<% end %>
</div> <!-- end task_list -->

Why do you use #tasks and not #project.tasks? because it would show the new task you created for your form?.
Well, you could still use #project.tasks and do something like:
<% #project.tasks.each do |task| %>
<% unless task.title.nil? %>
<div class="task">
<div class="completed"><%= task.completed %></div>
<div class="complete_link"><%= link_to "Good", complete_project_task_path(#project, task) %></div>
<div class="title"><%= link_to task.title, project_task_path(#project, task) %></div>
</div>
<% end %>
<% end %>
which would skip the tasks without the title.
The error you get is not because the #tasks variable is empty, because if it was, you would never come to the code below it. The error you get is that one of the items inside your #tasks contains a value it does not expect. I guess it has something to do with your complete_project_task_path(#project, task) that will check something of task that is not set correctly?

So i think i figured it out. (please correct me if im wrong)
Ruby variables hold references to
objects and the = operator copies the
references.
(from http://ruby-doc.org/docs/Newcomers/ruby.html#objects no. 17)
Taking that into account, look at the following example
#task_list = #project.tasks
This essentially makes #task_list refer to the same object as #project.tasks. If the data in #project.tasks changes, so does #task_list's data because they both point to the same object.
#project.tasks.build seems to alter the object, that both #project.taks and #task_list point to, in some way.
The solution was pretty simple. Instead of setting #task_list = #project.tasks i created a new object for #task_list.
So in the controller, istead of doing this:
#project = Project.find(params[:project_id])
#task_list = #project.tasks
just do it like this:
#project = Project.find(params[:project_id])
#taks_list = Task.where(:project_id => #project.id)

Related

Strange output from rails each do

Rails each do method is acting strangely and I do not know why.
controller
def index
#fabric_guides = FabricGuide.with_attached_image.all.order(:name)
end
index.html.erb
<div class="guide-items">
<%= #fabric_guides.each do |fabric| %>
<div class="guide-container">
<%= link_to fabric_guide_path(slug: fabric.slug) do %>
<%= image_tag fabric.image if fabric.image.attached? %>
<% end %>
<div class="guide-info">
<p class="g-name">
<%= link_to fabric.name,
fabric_guide_path(slug: fabric.slug) %>
</p>
</div>
</div>
<% end %>
</div>
I have two FabricGuide records so I expect two "guide-container" but I get three. Or more precisely I get two guide containers and a third block of text containing all the content from the last FabricGuide record.
I have almost an identical setup for articles and have never encountered this problem. I'd happily share more information if needed. Thank you!
Please remove = equal sign from your each loop of view code
like below :-
<% #fabric_guides.each do |fabric| %>
...
...
<% end %>
you have used this <%= #fabric_guides.each do |fabric| %> in your view that's why it shows all record in DOM.
The expression for erb tags is <% %>
now if we want to print that tag too then we apply <%= %>

Rails retrieving all records in show controller

In my rails category show controller for categories I have it setup like this
def show
#categories = Category.find_by(params[:name])
end
But when I visit this controller it returns all records of products found in the category instead of single category.
Here is the code in my view controller for category
<div class="grid">
<% #categories.products.each do |product| %>
<%= link_to product_path(id: product.slug, category_name: product.category.name), class: "card" do %>
<div class="product-image">
<%= image_tag product.productpic.url if product.productpic? %>
</div>
<div class="product-text">
<h2 class="product-title"> <%= product.name %></h2>
<h3 class="product-price">£<%= product.price %></h3>
</div>
<% end %>
<% end %>
</div>
What am i doing wrong here?
First of all, for security purposes, you should never trust the params hash to retrieve records. Rails will "make the data safe" if you use a hash as your arguments. Use this code below:
def show
#category = Category.find_by(name: params[:name])
end
Second, usually on a show page, you only want to retrieve one record and therefore the variable should be named as singular. I corrected that above.
Third, it helps if you use proper indenting when posting examples. It makes it easier for us to help you.
Fourth, the line below (I changed #categories to #category) is basically saying: "Now that I have this single category, find all the products associated with it in the products table and put them into the |product| variable for iteration"
<% #category.products.each do |product| %>
I'm not sure what you want to do with the category, but if you keep this line of code, it will always show you all the products. Maybe you only want to show the most recent 3, in which case you could do something like this:
In your controller:
def show
#category = Category.find_by(name: params[:name])
#recent_products = #category.products.order(created_at: :desc).limit(3)
end
In your view:
<div class="grid">
<% #recent_products.each do |product| %>
<%= link_to product_path(id: product.slug, category_name: product.category.name), class: "card" do %>
<div class="product-image">
<%= image_tag product.productpic.url if product.productpic? %>
</div>
<div class="product-text">
<h2 class="product-title"> <%= product.name %></h2>
<h3 class="product-price">£<%= product.price %></h3>
</div>
<% end %>
<% end %>
</div>
You can do this way
in your controller you can write this code
def show
#category = Category.find_by_name(params[:name])
end
and in your view it will work
<div class="grid">
<% #category.products.each do |product|%>
// place your code what you want to display
<% end %>
</div>
I hope it would help you and still if you have any concern please let me know.

Rails render not working as expected

Have a show action on a ticket that pulls in a list of comments on the ticket. My goal is to display the list of ticket comments and then above it give the option for someone to add a comments.
This works when the form is below the list of comments, however when i put the above the comments, i get an error in the render of the comments saying that the record has no user id. Almost like the #comments in my ticket controller is somehow getting this new thing added from the form even though it is instantiated before the render of the form.
Here are my partials and controller. The error when the form is displayed first is "unable to find user_id = 0" That is due to the comments.commenter, which looks for the name of the person submitting the comment.
Ticket Controller
def show
#ticket = Ticket.find(params[:id])
#comments = #ticket.comments
end
tickets/show - The form is in here twice, but i only put it in once when trying to get this to work. I want it to work in the top spot.
<div class="widget-content nopadding">
<ul class="new-comment">
<%= render "comments/form" %> --- Does not work here
</ul>
<ul class="recent-comments">
<%= render #comments %>
</ul>
</div>
<%= render "comments/form" %> --- Works here,
comments/form
<%= form_for([#ticket, #ticket.comments.build]) do |f| %>
<div class="field">
<%= f.label :content %><br>
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
comments/comment
<li>
<div class="comments">
<span class="user-info"> User: <%= comment.commenter %> </span>
<p>
<strong>Comment:</strong>
PUBLIC: <%= comment.try(:content) %>
</p>
</div>
</li>
Do you have a default set on the comment user_id to 0? What's happening is the form is building a new comment on the ticket. So i think that is getting rendered in the collection partial in addition to what the ticket already had. With the user_id set to zero, the comment.commenter tries to do the where id = 0, and it blows up.
So if you do have a default on that column, remove it. Foreign keys should default to nil.
The #comments is likely lazy loaded, that's why the _comment partial can be effected. #comments was not invoked until you have the second render. To avoid this, you can switch the #comments in the controller to:
#comments = Comment.where(ticket_id: params[:id])
Hope this made sense. Let me know if the default is the case. It's just a hunch :)

Check_box_tag not displaying labels properly

Everything is posting correctly, but I do not see any labels in my checkboxes, just blanks. My form looks like this:
<%= form_for #itemrecord do |f| %>
<div class="col-xs-12">
<p><b>Items people are asking for</b></p>
</div>
<% #wishlist.each do |category, list| %>
<div class="col-xs-2">
<div class="form-group box">
<h5> <%="#{category}"%> </h5>
<% list.each do |thing| %>
<%= check_box_tag ":item_name[]", "#{thing}" %>
</br>
<% end %>
</div>
</div>
<% end %>
<%= f.submit "Go!", class: "btn btn-primary btn-large btn-block" %>
</div>
<% end %>
What's happening is that wishlist is a hash of categories and items within those categories that I set in the controller, and is then called by multiple form builders to build checkboxes. The challenge is that right now in the current implementation, the checkboxes params are passed through properly (FYI controller code is at the bottom), but beside each checkbox, there is no text that shows the thing (i.e., no label so that people know what they're checking.
Here's the html generated for one checkbox (it's the same for all checkboxes)
Basically, I need to make the value the label.
FYI what's happening is that for every item checked, a record is being created. Here's the controller code:
def create
items_to_be_saved = []
inventory_params.each do |i|
items_to_be_saved << ({ :signup_id => Signup.find_by_email(session[:signup_email]).id, :item_name => i })
end
if Inventory.create items_to_be_saved
flash[:success] = "Thanks!"
redirect_to root_path
else
render new_inventory_path
end
end
def inventory_params
params.require(":item_name")
end
In your code:
<%= check_box_tag ":item_name[]", "#{thing}" %>
Second parameter for check_box_tag is not a label value, it is just a value which goes to controller in parameters. If you wan't to display label within your checkbox you will need to call label_tag in your view:
= label_tag ':item_name[]', thing
= check_box_tag ':item_name[]'
But you definitely should check simple_form gem which allows you to render checkboxes in much cleaner way:
f.input :field, as: :boolean, inline_label: 'Label'
can you please try it and let me know
<%= check_box_tag 'item_name[]', thing %>

getting a random product in rails

/products/index.html.erb
<div class="hide-for-small panel">
<h3>Sidebar</h3>
<h5 class="subheader">Feature Product</h5>
<% Product.random do | product | %>
<%= image_tag(Product.image_url) %>
<h5><%= link_to Product.title, product %></h5>
<p><%= button_to 'Add to Cart', line_items_path(:product_id => product) %></p>
<% end %>
</div>
/models/product.rb
def Product.random
self.limit(1).offset(rand(self.count)).first
end
Trying to pull a random product using Postgres. The query comes through in the console but I get no view results in the index.
Any solutions or different ways of accomplishing this?
The whole issue is you're passing a block to a method that doesn't take a block, so its silently ignored, and never executed, so the other problems you have don't come up.
All of your code sits inside a block which you're passing to Product.random via Product.random do |product|. That should be product = Product.random with no do/end block.
Once you've done this, you'll start seeing more errors as you're using Product.title instead of product.title etc.
It looks like your product in your image_tag and link are incorrectly referencing the model and not the random one.
Try changing these:
<%= image_tag(product.image_url) %>
<h5><%= link_to product.title, product %></h5>
You should really generate the random product in your controller and access an instance variable in your view.
Something like:
controller's index action:
#random_product = Product.random
view:
<%= image_tag(#random_product.image_url) %>
etc.
You should never access the model directly from the view.
First of all, you're mixing up class methods with instance methods. Try making the view:
<div class="hide-for-small panel">
<h3>Sidebar</h3>
<h5 class="subheader">Feature Product</h5>
<%= image_tag(#product.image_url) %>
<h5><%= link_to #product.title, #product %></h5>
<p><%= button_to 'Add to Cart', line_items_path(:product_id => #product.id) %></p>
</div>
Class methods (Product.random) are defined when you don't need a specific one. So product.title if for a specific product.
Second of all, in your controller for this action, you'll need to define the #product instance variable as #product = Product.random. And then you can use that in the view.
Third of all, getting a random record could be simplified to:
Product.order("RANDOM()").first
So your model code would look like:
def self.random
Product.order("RANDOM()").first
end

Resources