1
  • Hello, I'm relatively new to Turbo Streams and Frames, and I'm currently facing an issue with rendering replies to comments using Turbo Frames and Streams.

Let me explain my setup. I have a Comment model, and I've utilized the same table to include both comments and their respective replies. Below is how I've implemented it

  • Below is my comment.rb
    class Comment < ApplicationRecord
      belongs_to :post
      belongs_to :user
      has_many :likes, as: :likeable
      scope :ordered, -> { order(id: :desc) }
      belongs_to  :parent, class_name: 'Comment', optional: true
      has_many    :replies, class_name: 'Comment', foreign_key: :parent_id, dependent: :destroy
    
      validates :body, presence: true
    end
  • below is my posts show page where where i want to render comments all comments from

        <%= turbo_frame_tag "comments" do%>
          <%= render @comments, post: @post, comment: @comment %>
        <% end %>
    

    -Below is my comment partial where i want every parent comment to have its replies nested under it recursively

    <%= turbo_frame_tag comment do%>
      <div class="">
          <p class="mt-4 text-gray-500 dark:text-gray-400"><%= comment.body %></p>
        </div>
        <%= turbo_frame_tag dom_id(Comment.new)  do%>
          <%= render "comments/like_button", comment: comment%>
        <% end %>
        <% if comment.replies.any? %>
          <% comment.replies.each do |reply| %>
            <div class="ml-4">
              <%= turbo_frame_tag nested_dom_id(comment, "replies") do%>
                <%= render 'comments/comment', comment: reply %>
              <% end %>
            </div>
          <% end %>
        <% end %>
      </div>
    <% end %>
    

    app/views/comments/create_turbo.stream.erb

    <%= turbo_stream.update Comment.new, "" %>
    <% if @comment.parent_id.nil? %>
      <%= turbo_stream.prepend "comments", @comment%>
    <% else %>
      <% @comment.replies.each do |reply| %>
        <%= turbo_stream.prepend nested_dom_id(@comment, "replies") do%>
          <%= render 'comments/comment', comment: reply %>
        <% end %>
      <% end %>
    <% end %>
    
    • The problem is , the child comments only show up well nested after i refresh the page manually, which is not the case i want. what i want is that when i create a reply , i want it to get rendered under its parent without page refresh.
    • Any help will be appreciated
0

2 Answers 2

0

Like this:

# app/views/comments/index.html.erb

<%= link_to "New comment", new_comment_path, data: {turbo_frame: :new_comment} %>

<%= turbo_frame_tag :new_comment, class: "contents" %>

<%= tag.div id: :comments do %>
  <%= render @comments.where(parent_id: nil) %>
<% end %>
# app/views/comments/_comment.html.erb

<%= tag.div id: dom_id(comment) do %>
  <%= comment.body %>

  <%= link_to "Reply", new_comment_path(parent_id: comment),
    data: {turbo_frame: dom_id(comment, :new_comment)} %>

  <%= tag.div id: dom_id(comment, :comments), class: "pl-4" do %>
    <%= render comment.replies %>
  <% end %>

  <%= turbo_frame_tag comment, :new_comment, class: "contents" %>
<% end %>
# app/views/comments/new.html.erb

# grab parent_id from url params
<% @comment.parent_id ||= params[:parent_id] %>

<%= turbo_frame_tag *[@comment.parent, :new_comment].compact, class: "contents" do %>
  <%= render "form", comment: @comment %>
<% end %>
# app/views/comments/_form.html.erb

<%= form_with model: comment, class: "contents" do |f| %>
  <%= f.hidden_field :parent_id %>
  <%= f.text_area :body, placeholder: :comment %>

  <%= f.submit class: "btn-primary" %>
<% end %>
# app/controllers/comments_controller.rb

def create
  @comment = Comment.new(comment_params)

  respond_to do |format|
    if @comment.save
      format.turbo_stream do
        if @comment.parent
          render turbo_stream: [
            # remove reply form
            turbo_stream.update(helpers.dom_id(@comment.parent, :new_comment)),
            # add new reply under the corresponding parent comment
            turbo_stream.append(helpers.dom_id(@comment.parent, :comments), @comment),
          ]
        else
          render turbo_stream: [
            turbo_stream.update(:new_comment),
            turbo_stream.append(:comments, @comment),
          ]
        end
      end
      format.html { redirect_to comments_url, notice: "Created." }
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

Note, turbo-rails v1.5.0 updated turbo_frame_tag to generate the same id as dom_id:
https://github.com/hotwired/turbo-rails/pull/476

Before v1.5.0 you would do this:

<%= turbo_frame_tag :new_comment, comment, class: "contents" %>

<%= turbo_frame_tag *[:new_comment, @comment.parent].compact, class: "contents" do %>
  #...

Result:

result

<div>
  <a data-turbo-frame="new_comment" href="/comments/new">New comment</a>
  <turbo-frame class="contents" id="new_comment"></turbo-frame>

  <div id="comments">
    <div id="comment_39">
      first comment
      <a data-turbo-frame="new_comment_comment_39" href="/comments/new?parent_id=39">Reply</a>
      <div id="comments_comment_39" class="pl-4">
        <div id="comment_41">
          first reply
          <a data-turbo-frame="new_comment_comment_41" href="/comments/new?parent_id=41">Reply</a>
          <div id="comments_comment_41" class="pl-4"></div>
          <turbo-frame class="contents" id="new_comment_comment_41"></turbo-frame>
        </div>
        <div id="comment_42">
          second reply
          <a data-turbo-frame="new_comment_comment_42" href="/comments/new?parent_id=42">Reply</a>
          <div id="comments_comment_42" class="pl-4">
            <div id="comment_43">
              first reply to second reply
              <a data-turbo-frame="new_comment_comment_43" href="/comments/new?parent_id=43">Reply</a>
              <div id="comments_comment_43" class="pl-4"></div>
              <turbo-frame class="contents" id="new_comment_comment_43"></turbo-frame>
            </div>
          </div>
          <turbo-frame class="contents" id="new_comment_comment_42"></turbo-frame>
        </div>
      </div>
      <turbo-frame class="contents" id="new_comment_comment_39"></turbo-frame>
    </div>
    <div id="comment_40">
      second comment
      <a data-turbo-frame="new_comment_comment_40" href="/comments/new?parent_id=40">Reply</a>
      <div id="comments_comment_40" class="pl-4"></div>
      <turbo-frame class="contents" id="new_comment_comment_40"></turbo-frame>
    </div>
  </div>
</div>
Sign up to request clarification or add additional context in comments.

2 Comments

i tried this but the child comments just appear on top, yet i want them to get nested under thier parent
@mutebigodfrey see screenshot of the result ^. i'm specifically appending new replies at the bottom and under the corresponding parent comment: turbo_stream.append(helpers.dom_id(@comment.parent, :comments), @comment)
0
 - I made few changes to make it work
```
<% if @comment.parent_id.nil? %>
  <%= turbo_stream.prepend "comments" do %>
    <%= render @comment %>
  <% end %>
<% else %>
  <%= turbo_stream.after dom_id(@comment.parent) do %>
    <div class="ml-4">
      <%= render @comment %>
    </div>
  <% end %>
<% end %>
```
 - now i can have comments rendered recursively 

1 Comment

now i can have comments nested recursively

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.