Hotwire Discussion

Unable to append (or prepend) new content

I’ve been playing around with adding a Turbostreams into a project.

I have a page that displays all posts (post) with comments (comment) listed along side. Each post has_many comments.

I have them displayed something like this:
posts/_show.html.erb

  <div class="post__comments--inner">
      <%= render '/comments/show', post: post, comment: Comment.new %>
    </div>

comments/_show.html.erb

  <%= turbo_stream_from post, :comments %>
  <div class="comments__list">
    <%= render partial: "comments/comment", collection: post.comments %>
  </div>
  <div class="comments__form">
    <%= turbo_frame_tag "new_comment", src:  new_post_comment_path(post), target: "_top" %>
  </div>

comments_controller.rb

  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(:comments, partial: 'comments/comment',
                                                              locals: { comment: @comment })
        end
        format.html { redirect_to @comment.post.place }
      end
    end
  end

When submit a new comment on any post it gets persisted into the database, and the network returns a 200 POST request with this content:

<turbo-stream action="append" target="comments"><template>  <div class="comment" id="comment_61">
    <div class="comment__user">
          me
      </div>
    </div>
    <div class="comment__text">
      comment
    </div>
  </div>
</template></turbo-stream>

But it does not actually show up anywhere.

I’m wondering if the fact that I have multiple lists of comments on the page is causing problems, target="comments" isn’t specific to one comment field.

That is exactly the issue here, one feature of turbo is the ids must be unique, im guessing that there are multiple models that have the comments conatiner on the same page. to solve this i would do this inside the container for each post


  <div class="comments__list"  id="<%= dom_id(post, :comments)  %>">
    <%= render partial: "comments/comment", collection: post.comments %>
  </div>

this would yield comment containers with ids like

post_1_comments
post_2_comments
post_x_comments

this way you would always have unique ids

you will also need to gain access to the post that the comment belongs to, to pass it to the partial

        format.turbo_stream do
          render turbo_stream: turbo_stream.append(dom_id(@comment.post, :comments), partial: 'comments/comment',
                                                              locals: { comment: @comment })
        end
2 Likes

Thanks, the adding the dom_id into the controller solves everything!

1 Like

great!. happy it worked. remember, turbo relies heavily on IDS and if the id’s aren’t unique then you face weird bugs n things. as a rule of thumb, whenever you try finding yourself replacing or using frame. it’s always preferred to use dom_id it’s a very good little helper available to you.

happy coding!.

Yup. One of the lessons I keep learning when it comes to JavaScript is an invalid DOM will either give you no result or intermittently wrong results without explaining anything to you. If your script fails, check the HTML with a validator, like the one from W3.org.

Walter