Ruby on Rails. Views in polymorphic associations - ruby-on-rails

I develop site which has articles and news pages and I would like to add opportunity to comment both. I use polymorphic associations.
class Article < ActiveRecord::Base
has_many :commentaries, :as => :commentable
end
class News < ActiveRecord::Base
has_many :commentaries, :as => :commentable
end
class Commentary < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
I would like to show comments below commentable object
views/articles/show.html.erb
<p>
<b>Title:</b>
<%= #article.title %>
</p>
<p>
<b>Short text:</b>
<%= #article.short_text %>
</p>
<p>
<b>Full text:</b>
<%= #article.full_text %>
</p>
<%= render 'commentaries/form' %>
views/news/show.html.erb
<p>
<b>Title:</b>
<%= #news.title %>
</p>
<p>
<b>Text:</b>
<%= #news.text %>
</p>
<p>
<b>Created:</b>
<%= #news.created %>
</p>
views/commentaries/_form.html.erb
<h1>Comments</h1>
<ul id="comments">
<% #commentaries.each do |comment| %>
<li><%= comment.content %></li>
<% end %>
</ul>
<h2>New Comment</h2>
<% form_for [#commentable, Comment.new] do |form| %>
<ol class="formList">
<li>
<%= form.label :content %>
<%= form.text_area :content, :rows => 5 %>
</li>
<li><%= submit_tag "Add comment" %></li>
</ol>
<% end %>
And my controllers:
class CommentariesController < ApplicationController
def index
#commentable = find_commentable
#commentaries = #commentable.commentaries
end
end
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
end
end
When I go to mysite/article/1 I get error undefined method `each' for nil:NilClass, because there isn't #commentable in my article controller and commentaries controller's code doesn't execute.
How to execute index action of commentaries controller on article/show page?

Add local variable:commentable => #article, while rendering the commentaries form
<%= render 'commentaries/form', :commentable => #article %>
Access the local variable from you partial view views/commentaries/_form.html.erb
<% commentable.commentaries.each do |comment| %>
...
<% end %>
...
<% form_for [commentable, Comment.new] do |form| %>
...
<% end %>

Related

Error not displaying with form_with

Good Morning.
I'm following the tutorial http://edgeguides.rubyonrails.org/getting_started.html and I find the following:
I have a controller called ArticlesController. The create method uses the "if # article.save" statement to save the #article object, and if something goes wrong then render 'new'. Similarly the update method uses "if #article.update (article_params)" to update the record for that article, and if something goes wrong render 'edit'.
In new and edit views <% if # article.errors.any? %> is used to determine if there was any error and display the corresponding message, but the problem is that in the new view works fine, but in the edit view #article.errors.any view? returns false even if there is an error.
Can someone tell me what's wrong. Thank you very much.
Then I put the ArticlesController class, the views new.html.erb and edit.html.erb, and the article model.
class ArticlesController < ApplicationController
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
------------------------------------------------------------------
<h1>New Article</h1>
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
----------------------------------------------------------------------
<h1>Edit article</h1>
<%= form_with(model: #article) do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
---------------------------------------------------------------------
class Article < ApplicationRecord
validates :title, presence: true,
length: { minimum: 5 }
validates :text, presence: true
end
All forms generated by form_with will be submitted by an XHR (Ajax) request by default. if you want to disable remote forms then you can do it with local like you did in the form in new.html.erb.
change this in your edit.html.erb :
<%= form_with model: #article, local: true do |form| %>
It's a limitation of Turbolinks, which does not work with render
See here https://joey.io/turbolinks-rendering-form-errors/ and here https://github.com/jorgemanrubia/turbolinks_render
Got the same error today. They are using render with form_with in their documentation but it doesn't work.
I used form_for and it fixed the problem.
<%= form_for article do |form| %>

Nested form not displaying submitted content on show page

I am trying to add a polling feature to the app from Hartl's Rails Tutorial. Started off by following #196 Nested Model Form and Nested forms with Rails 4.2 Strong Parameters. After submitting the form, the content saves properly to the database, however, I am unable to see the results in the show page and cannot figure out why.
Models-
class Micropost < ActiveRecord::Base
has_many :polls, dependent: :destroy
accepts_nested_attributes_for :polls, :reject_if => lambda {|a| a[:content].blank? }
def questions_for_form
collection = polls.where(micropost_id: id)
collection.any? ? collection : polls.build
end
end
class Poll < ActiveRecord::Base
belongs_to :micropost
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda {|a| a[:content].blank? }
def answers_for_form
collection = answers.where(micropost_id: id)
collection.any? ? collection : answers.build
end
end
class Answer < ActiveRecord::Base
belongs_to :poll
end
Controllers-
class StaticPagesController < ApplicationController
def home
if logged_in?
#micropost = current_user.microposts.build
#poll = #micropost.polls.build
3.times {#poll.answers.build}
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
end
class MicropostsController < ApplicationController
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
private
def micropost_params
params.require(:micropost).permit(:content, polls_attributes: [:content, :id, :micropost_id, answers_attributes: [:content, :id, :poll_id]])
end
end
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
end
Routes-
Rails.application.routes.draw do
resources :microposts, only: [:create, :destroy]
resources :polls
resources :answers
end
Views-
_micropost_form.html.erb
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new post..." %>
</div>
<button type="button" class="btn btn-default btn-xs" data-toggle="collapse" data-target="#pollcollapse" id="pollbtn">Poll</button>
<div id="pollcollapse" class="collapse">
<%= f.fields_for :polls do |questions_for_form| %>
<%= render 'shared/poll_fields', :f => questions_for_form %>
<% end %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
_poll_fields.html.erb
<div class="field">
<%= f.text_field :content, placeholder: "Poll question..." %>
</div>
<%= f.fields_for :answers do |answers_for_form| %>
<%= render 'shared/answer_fields', :f => answers_for_form %>
<% end %>
_answer_fields.html.erb
<div>
<%= f.text_field :content, placeholder: "Answer" %>
</div>
show.html.erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
</aside>
<div class="col-md-8">
<% if #user.microposts.any? %>
<h3>Posts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
<span class="content"><%= micropost.content %></span>
<span>
<% for poll in #micropost.polls %> //Poll & answer content do not appear.
<%= poll.content %>
<% for answer in poll.answers %>
<%= answer.content %>
<% end %>
<% end %>
</span>
</li>
I think this should be all of the relevant information, but please let me know if I am missing anything. Thank you very much for any help at all!
In your _micropost.html.erb partial, you reference #micropost which doesn't appear to be defined anywhere. Try changing that from an instance variable to tbe local variable micropost (learn about the difference between #micropost and micropost here). Also, in Ruby it's recommended by people smarter than me (at least I've read it multiple times) that you should use .each instead of for. The updated code in your partial:
<li id="micropost-<%= micropost.id %>">
<span class="content"><%= micropost.content %></span>
<span>
<% micropost.polls.each do |poll| %>
<%= poll.content %>
<% poll.answers.each do |answer| %>
<%= answer.content %>
<% end %>
<% end %>
</span>
</li>
Also, since you define #microposts in your show action, you can refactor your code in the show view like so:
<div class="col-md-8">
<% if #microposts.any? %>
<h3>Posts (<%= #microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
Yes instance vs. Local variables issue. I'd say it might be easier to use a gen, but if you are learning rails and you have the time, my opinion is its better to do it yourself the first time. It helps you learn the framework and better able to solve problems when you run into something for which there is no gem. After learning yourself, go with a gem if you find it really serves your purpose... Or make your own gem! Cheers.

undefined method `comments_path' for #<#<Class:... > (RAILS)

I'm trying to add comments to my photos model however when I submit my comment I am getting the following error:
undefined method `comments_path' for #<#<Class:0x007fdef11c3dd8>:0x007fdef4163f98>
My routes look like this:
resources :photos do
resources :comments
end
Models:
class Comment < ActiveRecord::Base
belongs_to :photo
end
class Photo < ActiveRecord::Base
belongs_to :user
has_many :comments, dependent: :destroy
end
views new comment:
<%= form_for [#picture, #comment] do |f| %>
<%= f.label :comments %>
<%= f.text_area :comments %>
<%= f.submit 'Leave Comment' %>
<% end %>
views index:
<% if #photos.any? %>
<% #photos.each do |photo| %>
<%= link_to photo.title, photo_path(photo) %>
<%= photo.description %>
<% if photo.comments.any? %>
<ul>
<% photo.comments.each do |comment| %>
<li>
<%= comment.comments %>
</li>
<% end %>
<% else %>
<p> No comments yet </p>
<% end %>
<% if current_user %>
<%= link_to "Comment on #{photo.title}", new_photo_comment_path(photo) %>
<% if current_user.id == photo.user_id %>
<%= link_to "Delete #{photo.title}", photo_path(photo), method: :delete %>
<% end %>
<% end %>
<br>
<% end %>
<% else %>
No photos yet
<% end %>
<br>
<%= link_to "Add a photo", new_photo_path %>
Comments Controller:
class CommentsController < ApplicationController
def new
#photo = Photo.find(params[:photo_id])
#comment = Comment.new
end
def create
#photo = Photo.find(params[:photo_id])
#photo.comments.create(comment_params)
redirect_to photos_path
end
def review_params
params.require(:comment).permit(:comments)
end
end
Please let me know if you need any further info, haven't posted much so still getting used to the format. Thanks all.
EDIT: Here is the error I am getting in full:
Error message
Problem was in the comments view:
<%= form_for [#picture, #comment] do |f| %>
<%= f.label :comments %>
<%= f.text_area :comments %>
<%= f.submit 'Leave Comment' %>
<% end %>
#picture was meant to be #photo. doh.

Rails 3: Why am I not able to see the input fields that should be generated by form_for?

What I see
The following is my Track template:
app/views/tracks/index.html.erb
<h1>Playlist</h1>
<%= form_for #track do |f| %>
<%= f.error_messages %>
<p>
<%= f.text_field :youtube_url %>
<%= f.submit 'Add' %>
</p>
<% end %>
<hr />
<% if Track.first == nil %>
<p>Database is empty!</p>
<%else%>
<% #tracks.each do |track| %>
<ul>
<li><%= track.youtube_url %></li>
</ul>
<% end %>
<% end %>
And clearly, the input fields are not being shown for some reason.
The other relevant files:
app/controllers/tracks_controllers.rb
class TracksController < ApplicationController
def index
#track = Track.new
#tracks = Track.all
end
def create
#track = Track.new(params[:track])
render :action=>"index"
end
def delete
#track = Track.find(params[:id])
#track.destroy
redirect_to(tracks_url)
end
end
routes.rb
Music::Application.routes.draw do
resources :tracks
end
track.rb
class Track < ActiveRecord::Base
attr_accessible :title, :youtube_url
end
What am I doing wrong?
You have to put <%= opening tag in your form_for instead of <%.
<%= form_for #track do |f| %>
<%= f.error_messages %>
<p>
<%= f.text_field :youtube_url %>
<%= f.submit 'Add' %>
</p>
<% end %>

How to have a polymorphic model's partials associate to different things on the same page

Essentially, I have a binary voting system Like/Dislike. Thee class is called Like It has polymorphic associations to likeable:
class Like < ActiveRecord::Base
belongs_to :likeable, polymorphic: true
end
and we have the class Comment, which also has polymorphic associations to commentable and can be liked
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
has_many :likes, :as :likeable
end
We have the class Section, which can also be liked and commented on
class Section < ActiveRecord::Base
has_many :likes, as: :likeable
has_many :comments, as: commentable
end
However, on the page section#show I display the Section information, the section likes, and then the comments (from a comments/comments partial). Here is the Section#show view:
<h1><%= exercise.name %></h1>
<p><%= exercise.description %></p>
<%= render 'likes/like_button' %>
<%= render 'comments/comments' %>
<%= render 'comments/comment_form' %>
However, I want the ability to vote on each comment.
The following code is from the _comments.html.erb - What currently doesn't work is the rendering of the _like_button.html.erb because it doesn't apply to the comment at hand.
<% #comments.each do |comment| %>
<%= comment.content %>
<%= render 'likes/like_button' %>
<hr />
<% end %>
And here is the _like_button.html.erb partial
<% if #like.nil? %>
<%# No record of Like in table %>
<%= form_for [#likeable, Like.new] do |f| %>
<%= f.submit "Like" %>
<%= f.submit "Dislike" %>
<% end %>
<% else %>
<%# Marks current chosen option, if the opposite option is chosen, the record is updated to reflect the descion by the user %>
<%= form_for [#likeable, #like] do |f| %>
<% if #like.is_liked %>
Currently Liked!
<%= f.submit "Dislike" %>
<% else %>
<%= f.submit "Like" %>
Currently Disliked!
<% end %>
<% end %>
<% end %>
So ultimately, I just want to know how to make it possible to vote on a comment from within the Section#show view
Thanks!
Try this:
<% #comments.each do |comment| %>
<%= comment.content %>
<%= render 'likes/like_button', :like => comment.like, :likeable => comment %>
<hr />
<% end %>
<% if like.nil? %>
<%# No record of Like in table %>
<%= form_for [likeable, Like.new] do |f| %>
<%= f.submit "Like" %>
<%= f.submit "Dislike" %>
<% end %>
<% else %>
<%# Marks current chosen option, if the opposite option is chosen, the record is updated to reflect the descion by the user %>
<%= form_for [likeable, like] do |f| %>
<% if like.is_liked %>
Currently Liked!
<%= f.submit "Dislike" %>
<% else %>
<%= f.submit "Like" %>
Currently Disliked!
<% end %>
<% end %>
<% end %>

Resources