I have a system with posts and comments, each Post has_many Comments. I am trying to setup a turbostream so that when you post a comment it displays immediately.
Everything works in that data is persisted to the database but it seems like the turbo stream is not properly returning. When I hit the "comment" button nothing changes and I get a :no_content message for CommentsController#create
↳ app/controllers/comments_controller.rb:11:in `create'
[ActiveJob] Enqueued Turbo::Streams::ActionBroadcastJob (Job ID: b0be3a08-d7bb-4216-aac5-2f274a22dcbf) to Async(default) with arguments: "Z2lkOi8vY2lhby9Qb3N0LzM", {:action=>:append, :target=>"comments", :locals=>{:comment=>#<GlobalID:0x00007fa94123baf8 #uri=#<URI::GID gid://ciao/Comment/16>>}, :partial=>"comments/comment"}
[ActiveJob] Enqueued Turbo::Streams::ActionBroadcastJob (Job ID: 382e45b4-7a8f-4c8c-9e48-819fab0c19c4) to Async(default) with arguments: "Z2lkOi8vY2lhby9Qb3N0LzM", {:action=>:replace, :target=>#<GlobalID:0x00007fa9401ea938 #uri=#<URI::GID gid://ciao/Comment/16>>, :locals=>{:comment=>#<GlobalID:0x00007fa9401ea0f0 #uri=#<URI::GID gid://ciao/Comment/16>>}, :partial=>"comments/comment"}
No template found for CommentsController#create, rendering head :no_content
Completed 204 No Content in 37ms (ActiveRecord: 8.5ms | Allocations: 8931)
Turbo seems to work as far as creating the comment in the database, and sending back a comments POST request which I can see in the browser network tab. I'm wondering why there is :no_content and why the comments are not displaying until a page refresh.
Place.rb
class Post < ApplicationRecord
belongs_to :place
has_many :comments
broadcasts
end
Comment.rb
class Comment < ApplicationRecord
belongs_to :post
broadcasts_to :post
end
comment_controller.rb
def new
#comment = #post.comments.new
end
def create
#comment = current_user.comments.create!(comment_params)
respond_to do |format|
if #comment.save
format.turbo_stream do
render turbo_stream: turbo_stream.append(#comment, partial: 'comments/comment', locals: { comment: #comment })
end
format.html { redirect_to #comment.post.place }
end
end
end
On the post I render the comment as a partial:
<div class="post__comments--inner">
<%= render '/comments/show', post: post %>
</div>
Then comments/_show.html.erb
<%= turbo_stream_from post %>
<%= render post.comments %>
<% puts post.comments %>
<div class="comments__post">
<%= turbo_frame_tag "new_comment", src: new_post_comment_path(post), target: "_top" %>
</div>
_comment.html.erb
<div class="comment" id="<%= dom_id comment %>">
<%= comment.content %>
</div>
new.html.erb
<%= turbo_frame_tag "new_comment", target: "_top" do %>
<%= form_with model: [#comment.post, #comment], class: "comment-row__form",
data: { controller: "reset_form", action: "turbo:submit-end->reset_form#reset" } do |form| %>
<%= form.text_area :content, class: "comment-form--input form-control", data: {target: "comments.body"} %>
<%= form.hidden_field :post_id, value: #comment.post.id %>
<%= form.submit "comment", class: "btn btn-primary mt-2 mt-sm-0 ml-sm-3" %>
<% end %>
<% end %>
I think I may have found the issue but I'm not sure why it's happening the final think in the log is:
Turbo::StreamsChannel transmitting "<turbo-stream action=\"replace\" target=\"comment_36\"><template> <div class=\"comment\" id=\"comment_36\">\n <div class=\"comment__user\">\n
to my thinking it would be action=\"append\" rather than replace, especially since comment_36 does not currently exist on the page yet.
Try following this example from the docs:
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.append(:comments, partial: "comments/comment",
locals: { comment: #comment })
end
end
This should append it to a div in your html with an id of comments.
Related
I am fairly new to rails, so please bear with me. I have a very simple form that takes a couple values and sends it to my EmailController. I run very basic logic on it and just want to return the JSON to the view. It looks like the controller action is properly rendering the data (I can see in the server output), but the view doesn't change at all.
Here is my home.html.erb
<%= form_with(url: '/api/v1/emails', method: 'post') do |f| %>
<%= f.fields_for :email do |ev| %>
<%= ev.label :address %>
<%= ev.text_field :address %>
<% end %>
<%= f.submit 'Verify' %>
<% end %>
and emails_controller.rb (simplified)
class Api::V1::EmailController < Api::V1::BaseController
def create
#email = Email.new(email_params)
if #email.save
redirect_to api_v1_email_path(#email.id)
else
render json: #email.errors, status: :unprocessable_entity
end
end
def show
if #email
render 'show'
else
render json: #email, status: :not_found
end
end
def email_params
params.require(:email).permit(:address, :id)
end
It says it renders the template, but the view doesn't change:
Rendering api/v1/email/show.html.erb within layouts/application
Rendered api/v1/email/show.html.erb within layouts/application (0.9ms)
Completed 200 OK in 611ms (Views: 593.6ms | ActiveRecord: 1.3ms)
The template is just simple "hello world" plain text
Thank you in advance for the help!
I ended up having to create the form with local: true and it resolved the issue.
<%= form_with(url: '/api/v1/emails', method: 'post', local: true) do |f| %>
I'm trying to create a form that populates fields for each of the users pulled from a query, then submits all objects at once. With how my code is currently set up when the entire form submits only the non-hidden fields are sent to the controller.
<%= form_tag evaluations_path(method: :post) do |f| %>
<% Ingroup.where(group_id: Group.where(course_id: Project
.find(params[:project_id]).course_id)).each do |mem| %>
<h3>Evaluation for <%= "#{mem.user.Fname} #{mem.user.Lname}" %></h3>
<%= fields_for :evaluation do |form| %>
<% form.hidden_field :project_id, :value => params[:project_id] %>
<% form.hidden_field :user_id, :value => mem.user_id %>
<div class="field">
<%= form.label :score %>
<%= form.number_field :score %>
</div>
<div class="field">
<%= form.label :comment %>
<%= form.text_area :comment %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag "Submit" %>
</div>
<% end %>
EDIT: I added some additional code from the terminal and controller view.
EDIT 2: Removed the plural from evaluations and am still not seeing the hidden field values being passed.
Terminal Error:
Started POST "/evaluations?method=post" for 127.0.0.1 at 2020-12-02 21:55:08 -0500
Processing by EvaluationsController#create as HTML
Parameters: {"authenticity_token"=>"9pSgJJh3iE1doRy81Jhh3gHEgEJZt7pzcZw3C5EZeMEBh22VG8pmMKtHTwFml+Sj/XZmb3pBv6CmOLb9WvEVkQ==", "evaluation"=>{"score"=>"1", "name"=>"asdf"}, "commit"=>"Submit", "method"=>"post"}
(0.1ms) begin transaction
↳ app/controllers/evaluations_controller.rb:31:in `create'
Evaluation Exists? (0.2ms) SELECT 1 AS one FROM "evaluations" WHERE "evaluations"."user_id" IS NULL AND "evaluations"."project_id" IS NULL LIMIT ? [["LIMIT", 1]]
↳ app/controllers/evaluations_controller.rb:31:in `create'
(0.1ms) rollback transaction
↳ app/controllers/evaluations_controller.rb:31:in `create'
No template found for EvaluationsController#create, rendering head :no_content
Completed 204 No Content in 9ms (ActiveRecord: 0.3ms | Allocations: 5851)
Controller:
class EvaluationsController < ApplicationController
before_action :set_evaluation, only: [:show, :edit, :update, :destroy]
def new
#evaluation = Evaluation.new
end
def create
#evaluation = Evaluation.new(evaluation_params)
respond_to do |format|
if #evaluation.save
format.html { redirect_to #evaluation, notice: 'Evaluation was successfully created.' }
format.json { render :show, status: :created, location: #evaluation }
else
format.html { render :new }
format.json { render json: #evaluation.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_evaluation
#evaluation = Evaluation.find(params[:id])
end
# Only allow a list of trusted parameters through.
def evaluation_params
params.require(:evaluation).permit(:project_id, :user_id, :score, :name)
end
end
In your strong param says:
params.require(:evaluation).permit(:project_id, :user_id, :score, :name)
but in your view you have:
<%= fields_for :evaluations do |form| %>
Remove the final s for evaluations and it should work.
Related with your hidden fields, you're missing to use <%=. Try with:
<%= form.hidden_field :project_id, :value => params[:project_id] %>
<%= form.hidden_field :user_id, :value => mem.user_id %>
I am following a tutorial and we are creating a partial to to display a collection of data, but it is not rendering, my rails server command prompt shows that the database transaction is succesful:
Started GET "/portfolios" for ::1 at 2019-05-09 11:52:03 -0500
Processing by PortfoliosController#index as HTML
Rendering portfolios/index.html.erb within layouts/portfolio
Portfolio Load (0.3ms) SELECT "portfolios".* FROM "portfolios"
↳ app/views/portfolios/index.html.erb:5
Rendered collection of portfolios/_portfolio_item.html.erb [11 times] (2.1ms)
Rendered portfolios/index.html.erb within layouts/portfolio (5.3ms)
Rendered shared/_nav.html.erb (0.8ms)
Completed 200 OK in 31ms (Views: 28.6ms | ActiveRecord: 0.3ms)
Here is my partial under portfolios/_portfolio_item.html.erb:
<p><%= link_to portfolio_item.title, portfolio_show_path(portfolio_item) %></p>
<p><%= portfolio_item.subtitle %></p>
<p><%= portfolio_item.body %></p>
<%= image_tag portfolio_item.thumb_image unless portfolio_item.thumb_image.nil? %>
<%= link_to "Edit", edit_portfolio_path(portfolio_item) %>
<%= link_to 'Delete Portfolio Item', portfolio_path(portfolio_item), method: :delete, data: { confirm: 'Are you sure?' } %>
My index view under portfolios/index.html.erb :
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<%= render partial: 'portfolio_item', collection: #portfolio_items %>
And my portfolios_controller :
class PortfoliosController < ApplicationController
layout 'portfolio'
def index
#portfolio_items = Portfolio.all
end
def show
#portfolio_item = Portfolio.find(params[:id])
end
def new
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Your portfolio item is now live!' }
else
format.html { render :new }
end
end
end
def edit
#portfolio_item = Portfolio.find(params[:id])
end
def update
#portfolio_item = Portfolio.find(params[:id])
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'The record was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#portfolio_item = Portfolio.find(params[:id])
#portfolio_item.destroy
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Portfolio was successfully destroyed.' }
end
end
private
def portfolio_params
params.require(:portfolio).permit(:title,
:subtitle,
:body,
:main_image,
:thumb_image,
technologies_attributes: [:name])
end
end
If there is anything further needed please let me know and thanks in advance!
I'm not familiar with the collection syntax bbut for a quick fix try:
## index.html.erb
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<% #portfolio_items.each do |item| %>
<%= render partial: 'portfolio_item', portfolio_item: item %>
<% end %>
Or if you really want to be fancy, and your model is called Portfolio, rename your partial to portfolios/_portfolio.html.erb. That will let you do:
## index.html.erb
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<% #portfolio_items.each do |item| %>
<%= render item %>
<% end %>
Make sure there is no CSS or javascript that is removing/hiding your views. Also make sure you actually have some instances(rows) of the portfolio in your database.
<%= render partial: 'portfolio_item', collection: #portfolio_items %>
Although your usage of collection in partial is correct you can try to pass local variables and see if it works, because collection usage is dependent on file/model names which could cause issues.
When you use collection: #portfolio_items then you get access to use #portfolio_items instance variable within the partial view (portfolios/_portfolio_item.html.erb in your case).You are passing #portfolio_items but not using it anywhere, instead you are using portfolio_item variable which is not available in the partial view.
You need to iterate over #portfolio_items in order to render your items. If you need to pass variables to your partial use locals. Update your portfolios/index.html.erb as below
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<% #portfolio_items.each do |portfolio_item| %>
<%= render partial: 'portfolio_item', locals: { portfolio_item: portfolio_item } %>
<% end %>
I used this tutorial for making polymorphic comments
https://gorails.com/episodes/comments-with-polymorphic-associations
It works fine but when I set remote true to my comments form I got 505 error(the new comment is not appended to the list) when trying to add a new comment.
But when I reload the page the new comment appears in the list.
What can be wrong here?
I got this files in views/comments folder:
create.js
<% unless #comment.errors.any? %>
$("ul.comments").append('<%= j render "comments/comment" %>')
$("#new_comment")[0].reset();
<% end %>
_form.html.erb
<%= simple_form_for([commentable, Comment.new], remote: true) do |f| %>
<%= f.input :body, label: false %>
<%= f.button :submit, "Submit" %>
<% end %>
comments_controller.rb
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
#comment.save
respond_to do |format|
format.html { redirect_to #commentable, notice: "Your comment was successfully added."}
format.js
end
end
_comment.html.erb
<li class="comment">
<b><%= comment.user.try(:username) %>:</b>
<%= comment.body%>
</li>
console
UP
I got this routes for post
resources :posts do
resources :comments, module: :posts
end
+
controllers/posts/comments_controller
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
#commentable = Post.friendly.find(params[:post_id])
end
end
The folder structure looks almost the same as here
https://github.com/excid3/gorails-episode-36/tree/master/app/controllers
the response tab shows this
undefined local variable or method `comment' for #<#<Class:0x007f993d3c5450>:0x007f99450a6190>
Trace of template inclusion: app/views/comments/create.js.erb
and when I replace
<%= j render #comment %> with some text it appends to the list on submit
ok the problem is with _comment.html erb
updated create.js with instance variable for comment and it works now.
<% unless #comment.errors.any? %>
$("ul.comments").append('<%= j render partial: "comments/comment", locals:{comment: #comment} %>')
$("#new_comment")[0].reset();
<% end %>
I'm new to rails.
And to web development at all.
Sorry if some questions might seem dumb.
Trying to follow this screen cast - http://emersonlackey.com/screencasts/rails-3-with-paperclip.mov
But stopped at the problem - when i try to upload an image i get the following error :
ActiveRecord::UnknownAttributeError in PostsController#update
Unknown attribute: image
altough post_controller.rb seems ok (checked many times - it is the same as https://github.com/Emerson/Multiple-File-Uploads-with-Paperclip-and-Rails-3) :
Tried googlin of course, but didn't find anything.
Has anyone been trough this tutorial and had this problem ?
Problem fixed, the _form code, was incorrect!
I had to change:
<%= f.fields_for :assets do |asset| %>
to
<%= f.fields_for :assets do |asset_fields| %>
and
<%= asset.file_field :image %>
to
<%= asset_fields.file_field :asset %>
And it worked.
The reasons was quite silly, i just didn't the watch the screencast till the end, because I stopped at the middle - when the problem showed-up, and spent my whole attention googling for the solution.
Beginners mistake!
Posts model:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :assets_attributes
has_many :assets
accepts_nested_attributes_for :assets, :allow_destroy => true
end
Assets model:
class Asset < ActiveRecord::Base
belongs_to :post
has_attached_file :asset, :styles => { :large => "640x480", :medium=>"300x300>",
:thumb => "100x100>" }
end
Post controller:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
5.times { #post.assets.build }
end
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post, :notice => "Successfully created post."
else
render :action => 'new'
end
end
def edit
#post = Post.find(params[:id])
5.times { #post.assets.build }
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to #post, :notice => "Successfully updated post."
else
render :action => 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_url, :notice => "Successfully destroyed post."
end
end
_form:
<%= form_for #post, :html => { :multipart => true } do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content %><br />
<%= f.text_area :content %>
</p>
<%= f.fields_for :assets do |asset| %>
<% if asset.object.new_record? %>
<%= asset.file_field :image %>
<% end %>
<% end %>
<p><%= f.submit %></p>
<% end %>
Model index:
<% title "Posts" %>
<p><%= link_to "New Post", new_post_path %></p>
<hr />
<% for post in #posts %>
<div class="post">
<h2><%= link_to post.title, post%></h2>
<p class="content">
<%= post.content.html_safe %>
<br /><br />
<%= link_to "Edit", edit_post_path(post) %> | <%= link_to "Destroy", post,
:confirm => 'Are you sure?', :method => :delete %>
</p>
</div>
<% end %>
The error:
Started PUT "/posts/1" for 127.0.0.1 at Tue Sep 20 11:00:52 +0300 2011
Processing by PostsController#update as HTML
Parameters: {"commit"=>"Update Post", "post"=>{"title"=>"AAAAA", "content"=>"T
he enormous success of DropBox clearly shows that there's a huge need for simple
and fast file sharing.\r\n\r\nOur app will have the following features:\r\n\r\n
simple user authentication\r\n upload files and save them in Amazon S3\r\
n create folders and organize\r\n share folders with other users\r\n\r\nTh
roughout the tutorial, I will point out different ways that we can improve our a
pp. Along the way, we'll review a variety of concepts, including Rails scaffoldi
ng and AJAX.", "assets_attributes"=>{"0"=>{"image"=>#<ActionDispatch::Http::Uplo
adedFile:0x436fda0 #tempfile=#<File:C:/DOCUME~1/emve/LOCALS~1/Temp/RackMultipart
20110920-8900-zgz1ej-0>, #headers="Content-Disposition: form-data; name=\"post[a
ssets_attributes][0][image]\"; filename=\"02.jpg\"\r\nContent-Type: image/jpeg\r
\n", #content_type="image/jpeg", #original_filename="02.jpg">}}}, "authenticity_
token"=>"WHsbBak0O2xYBFe/h82+4/5aV2VPzHDdXcgb4QYmC4A=", "utf8"=>"Ō£ō", "id"=>"1"
}
←[1m←[35mPost Load (0.0ms)←[0m SELECT "posts".* FROM "posts" WHERE "posts"."i
d" = ? LIMIT 1 [["id", "1"]]
Completed 500 Internal Server Error in 63ms
ActiveRecord::UnknownAttributeError (unknown attribute: image):
app/controllers/posts_controller.rb:32:in `update'
Rendered C:/RailsInstaller/Ruby1.8.7/lib/ruby/gems/1.8/gems/actionpack-3.1.0/lib
/action_dispatch/middleware/templates/rescues/_trace.erb (0.0ms)
Rendered C:/RailsInstaller/Ruby1.8.7/lib/ruby/gems/1.8/gems/actionpack-3.1.0/lib
/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.0ms)
Rendered C:/RailsInstaller/Ruby1.8.7/lib/ruby/gems/1.8/gems/actionpack-3.1.0/lib
/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/lay
out (46.9ms)