Looping through a partial - ruby-on-rails-5

I'm creating a blog and on the index I'm looping through a partial for each article so I can show the post without a comments section, it looks like this:
<% render partial: 'view_post_only', collection: #articles %>
Which takes you to the partial _view_post_only:
<h1><%= link_to #article.title, article_path %></h1>
<p>
Written by <%= link_to #article.author.username, author_path(#article.author_id), class: "article-listing" %>
</p>
<% if #article.image.exists? %>
<p><%= image_tag #article.image.url %></p>
<% end %>
<p class="article-body"><%= #article.body %></p>
I'm getting this error on the first line of view_post_only:
No route matches {:action=>"show", :controller=>"articles"} missing required keys: [:id]
and lastly, included in my routes.db is:
resources :articles do
resources :comments
end

To resolve your first error, change:
<h1><%= link_to #article.title, article_path %></h1>
To:
<h1><%= link_to #article.title, article_path(#article) %></h1>
The article_path helper needs an instance of the resource you're attempting to link to so it knows where to go.
Also, the instance of each of your "articles" is not saved in the #article variable. By convention rails will singularize the partial's name and give you a method to access the item. In this case that convention would yield a method called view_post_only (I think). So you could replace #article with view_post_only in your template. Like so:
<h1><%= link_to view_post_only.title, article_path(view_post_only) %></h1>
This convention doesn't seem to work well in this case. To make your partial read better I'd be explicit about what you want to call the item:
<% render partial: 'view_post_only', collection: #articles, as: :article %>
and then replace your references to #article with just article in _view_post_only.

Related

Passing an instance variable into a form - rails

This is likely an error due to my minimal understanding of Rails and how to use variables across models, so if there is more code needed to answer it or if my terminology is incorrect, let me know and I will gladly update the question.
I have a feed of posts that I want a user to be able to "like." While the following code allows likes to work on an individual post's page - site.com:3000/posts/*post.id* - with the form data being passed of like[liked_post_id]:*post.id*, when I try to submit a like on a profile - site.com:3000/users/*user.id* - which contains a feed of posts, the form data being passed is like[liked_post_id]: (blank value)
How can I pass the post's ID within a feed of posts to the liked_post_id variable in _like.html.erb?
I have noticed that the action of the like form is /likes across the board. Would this will only work when you are on the page site.com:3000/posts/*post.id*? I'm curious if I need to modify the it so that the action of the form is /posts/*post.id*/likes when you are on the page site.com:3000/users/*user.id*
From my post view:
#views/posts/_post.html.erb:
...
<%= render 'posts/like_form' if signed_in? %>
...
Route to proper form:
#views/posts/_like_form.html.erb:
<div id="like_form">
<% if current_user.likes_this?(#post) %>
<%= render "posts/unlike" %>
<% else %>
<%= render "posts/like" %>
<% end %>
</div>
Like from:
#views/posts/_like.html.erb
<%= form_for Like.new, :remote => true do |f| %>
<%= f.hidden_field :liked_post_id, :value => #post.id %>
<%= f.submit "Like" %>
<% end %>
From profile (feed of posts):
#views/users/show.html.erb
...
<%= render #posts %>
...
Likes controller:
#controllers/likes_controller.rb
class LikesController < ApplicationController
before_filter :signed_in_user
def create
#post = Post.find(params[:like][:liked_post_id])
current_user.like!(#post)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
end
...
User model:
#models/user.rb
...
def like!(post)
likes.create!(liked_post_id: post.id)
end
...
#frank-blizzard has pointed out that my form markup is an issue. On a post's page the generated markup is:
<input id="like_liked_post_id" name="like[liked_post_id]" type="hidden" value="73" />
While on the feed page:
<input id="like_liked_post_id" name="like[liked_post_id]" type="hidden" />
You can do something like this:
<% form_for Like.new, :remote => true do |f| %>
<%= f.hidden_field :post_id, :value => #post.id %>
<%= f.submit %>
<% end %>
The current_user.likes.build(...) part should get out of your view and inside your controller. You are using a current_user.like! method so I guess you have implemented already some method in user model to accomplish this. If not build your like in the create action of LikesController where you can access params[:like].
def create
#post = Post.find(params[:like][:post_id])
current_user.likes.build(#post)
# ...
end
EDiT
You might need to pass your #post variable correctly into your _like_form partials, like so:
#views/posts/_post.html.erb:
...
<% if signed_in? %>
<%= render 'posts/like_form', :post => #post %>
<% end %>
...
This will give you acceess to a post variable inside the partial so you can prepopulate your forms value with its id. See this questions as well Pass a variable into a partial, rails 3? and make sure to read up on how to pass variables correctly to partials. you can debug your views using <%= debug <variablename> %>

Friendly_ID Ruby on Rails

The URL still shows the id and not the title even after using slug.
Code as follows
index.html.erb
<title>Blog!</title>
<h1>List of the Posts</h1>
<% #posts.each do |post| %>
<%= link_to post.title,:id => post.slug%>
<p><%= post.content %></p>
<%= link_to "Edit",edit_post_path(post) %> |
<%= link_to "Delete",post,:confirm=>"Are you sure ?",:method=>:delete %>
<hr />
<% end %>
<p><%= link_to "Add a New Post",new_post_path %></p>
posts_controller.rb
class PostsController < ApplicationController
def index
#posts=Post.all
end
def show
#posts=Post.find(params[:id])
end
end
Post Model
extend FriendlyId
friendly_id :title,use: :slugged
def should_generate_new_friendly_id?
new_record
end
routes.rb
Blog::Application.routes.draw do
get "blog/posts"
resources :posts
end
I would want the link to be 'localhost:8080/posts/this+is+the+title' and not 'localhost:8080/posts/2'
I was having trouble with this issue too. When I linked to the show action of my resource, I would get the id in the url instead of my slug. Although I could type in the slugged url and it would also work fine (I just couldn't link to the slugged url). It turns out that I had to use named route helpers for friendly_id to display the slug in the url (I was using the old-school controller: 'posts', action: 'show', id: post.id in my link_to helper). In your case, I would try changing:
<%= link_to post.title, :id => post.slug %>
to
<%= link_to post.title, post_path(post) %>
Also, friendly_id version 5.0 requires that you change Model.find to Model.friendly.find in your controller (unless you explicitly override it config/initializers/friendly_id.rb. Since this is an older post, it might not apply to you, but I thought I'd add it anyway. Try changing:
def show
#post = Post.find(params[:id])
end
to
def show
#post = Post.friendly.find(params[:id])
end
Hope that helps!

rails routes - Resource not appending _path

I am attempting to use resources to auto generate routes for my resource. The namespace is admin and the resource is author. The following code seems to work for most instances.
namespace :admin do
resources :author
end
When I run
rake routes
I get the following
admin_author_index GET /admin/author(.:format) admin/author#index
POST /admin/author(.:format) admin/author#create
new_admin_author GET /admin/author/new(.:format) admin/author#new
edit_admin_author GET /admin/author/:id/edit(.:format) admin/author#edit
admin_author GET /admin/author/:id(.:format) admin/author#show
PUT /admin/author/:id(.:format) admin/author#update
DELETE /admin/author/:id(.:format) admin/author#destroy
From what I can tell I am expecting the named paths to have a
_path
at the end. I am rather green at this. I have searched and searched but I could just be using the wrong terms to find the answer. Any help is appreciated. Thanks!
-edit- I should add that
<%= form_for [:admin, #author] do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.submit %>
<% end %>
Gives me errors saying it can not find admin_author_path
You can append either _path or _url to these. Basically everything looks good.
So for example
admin_author_index GET /admin/author(.:format) admin/author#index
the helper method can be admin_author_index_path or admin_author_index_url. These helpers can be used in controllers and views to point to a controller/action or url. Read this link http://guides.rubyonrails.org/routing.html to understand more.
No. The route name does not have the _path suffix.
Refer to Rails routing from inside in for more information. It explains routing in great detail.

Rails search functionality

I am taking a rails class at my University and I am trying to create a search form which will show the results on the same page rather than show a different page of results. Is this something simple to do? I am creating a museum app with artifacts for each museum but I want the user to search artifacts from either page.
On my routes.rb I have
resources :artifacts do
collection do
get 'search'
end
end
On my museum index I have the code below that he gave us but not sure how to tweak the get routes for the same page.
<%= form_tag search_artifacts_path, :method => 'get' do %>
<p>
<%= text_field_tag :search_text, params[:search_text] %>
<%= submit_tag 'Search' %>
</p>
<% end %>
<% if #artifacts %>
<p> <%= #artifacts.length %> matching artifacts. </p>
<h2> Matching Artifacts </h2>
<% #artifacts.each do |a| %>
<%= link_to "#{a.name} (#{a.year})", a %><br />
<% end %>
<% end %>
Yes, this is easy. Just have the index page return the search results if params[:search_text] is present - this way you don't need a new route or a different page.
class ArtifactsController < ApplicationController
def index
#artifacts = Artifact.search(params[:search_text])
end
end
class Artifact < ActiveRecord::Base
def self.search(query)
if query
where('name ILIKE ?', "%#{query}%")
else
all
end
end
end
So then your form looks like:
<%= form_tag artifacts_path, :method => 'get' do %>
<p>
<%= text_field_tag :search_text, params[:search_text] %>
<%= submit_tag 'Search' %>
</p>
<% end %>
Edit:
So what you really want to do is any page you want to search, include a form which makes a request to that same page.
Then in each of those controller methods just put this line of code:
#artifacts = Artifact.search(params[:search_text])
and that will populate the #artifcats array with only artifacts that match the search query.
Try using "Ransack" gem. It can also perform some more powerful searches.

How to get correct children ids using fields_for "parents[]", parent do |f| using f.fields_for :children, child?

I'm editing multiple instances of a parent model in an index view in one form, as in Railscasts #198.
Each parent has_many :children and accepts_nested_attributes_for :children, as in Railscasts #196 and #197
<%= form_tag %>
<% for parent in #parents %>
<%= fields_for "parents[]", parent do |f|
<%= f.text_field :job %>
<%= f.fields_for :children do |cf| %>
<% cf.text_field :chore %>
<% end %>
<% end %>
<% end %>
<% end %>
Given parent.id==1
f.text_field :job correctly generates
<input id="parents_1_job" type="text" value="coding" size="30" name="parents[1][job]">
But cf.text_field :chore generates ids and names that don't have the parent index.
id="parents_children_attributes_0_chore"
name="parents[children_attributes][0][chore]"
If I try passing the specific child object to f.fields_for like this:
<% for child in parent.children %>
<%= f.fields_for :children, child do |cf| %>
<%= cf.text_field :chore %>
<% end %>
<% end %>
I get the same. If I change the method from :children to "[]children" I get
id="parents_1___children_chore"
which gets the right parent_index but doesn't provide an array slot for the child index.
"[]children[]" isn't right either:
id="parents_1__children_3_chore"
as I was expecting attributes_0_chore instead of 3_chore.
Do I need to directly modify an attribute of the FormBuilder object, or subclass FormBuilder to make this work, or is there a syntax that fits this situation?
Thanks for any thoughts.
I did solve this problem by reading the source code for FormBuilder.fields_for
One possible answer: Yes, modify the f.object_name attribute of the FormBuilder object.
Specifically in this situation
f.fields_for :children
is going to call
f.fields_for_with_nested_attributes
which sets the name variable based on f.object_name. The id for the generated element looks like it is based on the name,so both match in the resulting html.
def fields_for_with_nested_attributes(association_name, args, block)
name = "#{object_name}[#{association_name}_attributes]"
.....
So one way to tell f.fields_for to do what I wanted is to set f.object_name to include the parent id for the duration of the f.fields_for block
<% old_object_name = f.object_name %>
<% f.object_name="parents[#{f.object.id}]" %>
<% =f.fields_for :children do |cf| %>
<%= cf.text_field :chore %>
<% end %>
<% f.object_name=old_object_name #should be "parents[]" %>
Then everything within the f.fields_for block can use the standard rails helpers unmodified.