I'm very new to Rails. I've found this strange issue related to turbo_frame and multiple new forms.
I have a model Post
with comments
and when I try to load multiple forms the value from one form appears on the others even if I specify to use Comment.new
###posts/index.html.erb
<div id="posts">
<% @posts.each do |post| %>
<div class="post">
<%= turbo_frame_tag dom_id(post) do %>
<%= render 'posts/show', post: post)%>
<% end %>
</div>
<hr>
<% end %>
</div>
### posts/_show.html.erb
<% post.comments.each do |comment| %>
<%= render "comments/comment", post: post, comment: comment %>
<% end %>
<%= turbo_frame_tag "comments_#{post.id}" do %>
<% end %>
<%= render "comments/add_new_comment", post: post, comment: Comment.new %>
### _add_new_comment.html.erb
<%= turbo_frame_tag "comments_new#{post.id}" do %>
<%= link_to "Add comment", open_create_new_comment_path(post) %>
<% end %>
### comments/_comment.html.erb
<%= turbo_frame_tag dom_id(comment) do %>
<div>
<%= comment.body %>
<div>
<%= link_to 'Edit', edit_post_comment_path(comment.post, comment) %>
<%= link_to 'Delete', post_comment_path(comment.post, comment),
method: :delete,
data: { confirm: 'Are you sure?', turbo_method: :delete } %>
</div>
</div>
<% end %>
### comments/_form.html.erb
<%= turbo_frame_tag "new_comment_form_#{post.id}" do %>
<%= form_with(model: [post, Comment.new]) do |form| %>
<%= form.rich_text_area :body %>
<%= form.hidden_field :post_id, value: post.id %>
<%= form.submit "Submit", data: { disable_with: "Submitting..." } %>
<% end %>
<% end %>
And finally the controller
class CommentsController < ApplicationController
before_action :set_post
before_action :set_comment, only: [:edit, :update, :destroy]
def edit
render turbo_stream: turbo_stream.replace("comment_#{params[:id]}", partial: "comments/edit_form", locals: {post: @post, comment: @comment})
end
def open_create_new
render turbo_stream: turbo_stream.replace("comments_new#{@post.id}", partial: "comments/form", locals: {post: @post, comment: Comment.new})
end
def create
@comment = Comment.new(comment_params)
if @comment.save
respond_to do |format|
format.turbo_stream do
render turbo_stream: [
turbo_stream.prepend("comments_#{@post.id}", partial: "comments/comment", locals: {comment: @comment}),
turbo_stream.replace("new_comment_form_#{@post.id}", partial: "comments/add_new_comment", locals: {post: @post})
]
end
end
else
respond_to do |format|
format.turbo_stream { render turbo_stream:turbo_stream.replace("new_comment_form_#{@post.id}", partial: "comments/add_new_comment", locals: {post: @post})}
end
end
end
# PATCH/PUT /comments/1 or /comments/1.json
def update
if @comment.update(comment_params)
render turbo_stream: turbo_stream.replace("edit_comment_form_#{@comment.id}", partial: "comments/comment", locals: {comment: @comment})
else
render turbo_stream: turbo_stream.replace("edit_comment_form_#{@comment.id}", partial: "comments/comment", locals: {comment: @comment})
end
end
# DELETE /comments/1 or /comments/1.json
def destroy
@comment.destroy
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.remove("comment_#{params[:id]}")
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
@comment = Comment.find(params[:id])
end
def set_post
@post = Post.friendly.find_by(slug: params[:post_id])
end
# Only allow a list of trusted parameters through.
def comment_params
params.require(:comment).permit(:body, :post_id, :parent_id)
end
end
The strange is each form works perfectly individually, but if I load the form from post 1 input some text, and then open the form to add comments to the other's posts the text is the same as I put in form 1, and when I try to submit the form the logs say the body is empty.
Ideas ? why the content from one is added to the others when I put all in locals
and use Comment.new
This is a javascript issue with rich text field, you need to have unique ids for every rich text field on the page, you can use :namespace
option of form_with
helper:
<%= form_with model: [post, Comment.new], namespace: dom_id(post) do |form| %>
<%= form.rich_text_area :body %>
<% end %>
<input type="hidden" name="comment[body]" id="post_2_comment_body_trix_input_comment" autocomplete="off">
<!-- namespace: ^^^^^^ -->
Drop turbo_frame_tag
s, you're not actually using them:
<!-- app/views/posts/index.html.erb -->
<div id="posts">
<%= render @posts %>
</div>
<!-- app/views/posts/_post.html.erb -->
<div id="<%= dom_id post %>">
<!-- will append post's comments here -->
<div id="<%= dom_id post, :comments %>">
<%= render post.comments %>
</div>
<!-- will update with new comment form then back to link -->
<div id="<%= dom_id post, :new_comment %>">
<%= link_to "Add comment", new_post_comment_path(post), data: {turbo_stream: true} %>
</div>
</div>
<!-- app/views/comments/_form.html.erb -->
<%= form_with model: [post, Comment.new], namespace: dom_id(post) do |form| %>
<%= form.rich_text_area :body %>
<%= form.submit "Submit", data: {turbo_submits_with: "Submitting..."} %>
<% end %>
<!-- app/views/comments/_comment.html.erb -->
<div id="<%= dom_id comment %>">
<%= comment.body %>
<div>
<%= link_to "Edit", edit_post_comment_path(comment.post, comment), data: {turbo_stream: true} %>
<%= link_to "Delete", post_comment_path(comment.post, comment), data: {turbo_confirm: "Are you sure?", turbo_method: :delete} %>
</div>
</div>
# app/controllers/comments_controller.rb
def new
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.update(
[@post, :new_comment],
partial: "comments/form",
locals: {post: @post, comment: Comment.new}
)
end
end
end
def create
@comment = @post.comments.new(comment_params)
if @comment.save
respond_to do |format|
format.turbo_stream do
render turbo_stream: [
turbo_stream.append(
[@post, :comments],
partial: "comments/comment",
locals: {comment: @comment}
),
turbo_stream.update(
[@post, :new_comment],
helpers.link_to("Add comment", new_post_comment_path(@post), data: {turbo_stream: true})
)
]
end
end
else
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.update(
[@post, :new_comments],
partial: "comments/form",
locals: {comment: @comment, post: @post}
)
end
end
end
end
def edit
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.update(
@comment,
partial: "comments/form",
locals: {comment: @comment, post: @post}
)
end
end
end
def update
respond_to do |format|
if @comment.update(comment_params)
format.turbo_stream do
render turbo_stream: turbo_stream.update(@comment)
end
else
format.turbo_stream do
render turbo_stream: turbo_stream.update(
@comment,
partial: "comments/form",
locals: {comment: @comment, post: @post}
)
end
end
end
end
def destroy
@comment.destroy
respond_to do |format|
format.turbo_stream { render turbo_stream: turbo_stream.remove(@comment) }
end
end