3

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.

tfantina
  • 788
  • 11
  • 37
  • You are not rendering anythning in the block , e.g format.turbo_stream { render turbo_stream: turbo_stream.append(@comment) } – Joel Blum May 01 '21 at 14:22
  • I was just copying from the hotwire demo where they have: `respond_to do |format|` `format.turbo_stream` `format.html { redirect_to @room }` `end` I have added a render block and it is returning successfully but nothing is being appended. I have updated my question to reflect the changes. – tfantina May 01 '21 at 19:55
  • it indeed needs to be append since its a new comment – Joel Blum May 01 '21 at 20:14
  • That's why I'm confused about why the turbo streams channel is saying the action is replace. – tfantina May 01 '21 at 20:28
  • I may have given a bad snippet above, try my answer perhaps – Joel Blum May 01 '21 at 20:54

1 Answers1

4

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.

Joel Blum
  • 7,750
  • 10
  • 41
  • 60
  • Thanks your original comment was an important step in the right direction, I think the reason nothing is still rendering has to do with the specify of the `target="comments"` since each post on the page has a comments field I don't think Turbo knows where to append. – tfantina May 01 '21 at 21:06
  • Indeed it is looking for an element with a dom id comments to append to. – Joel Blum May 01 '21 at 21:11
  • If it helps anyone else, don't forget to include `render turbo_stream:` – James Klein Sep 22 '22 at 21:51