Activity
I second @miguel's recommendation for the post from Thoughtbot. Insanely great implementation!
Totally agree with this comment and also feedback for all the publishers (ie Chris could do this also).
Such a great tutorial. Small enhancement for the form validation errors.
In the _inline_fields.html.erb
I added my render for the validation_errors (similar to other tutorials) so that the flash message is shown on an invalid save.
<% frame_id = dom_id(f.object, "#{ attribute }_turbo_frame") %>
<%= turbo_frame_tag frame_id, class: "contents" do %>
<%= render "validation_errors", model: f.object %>
<div class="flex flex-row items-center mt-4">
<%= yield %>
<%= f.button class: "ml-2 btn-primary inline-action" do %>
Save <%= f.object.class.human_attribute_name(attribute) %>
<% end %>
<%= link_to "Cancel", polymorphic_path(f.object), class: "cancel inline-action" %>
</div>
<% end %>
Using tailwindcss in the new rails 7, turbo and stimulus world. I was able to achieve this like:
Note the use of group
on the parent object (the submit button) and then the hidden
, group-disabled:hidden
and group-disabled:flex
on the child spans.
<%= f.button class: "btn-primary group" do %>
<span class="group-disabled:hidden h-4">Go!</span>
<span class="hidden group-disabled:flex">
<svg aria-hidden="true" class="mr-2 w-4 h-4 text-yellow-500 animate-spin fill-white" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
Processing...
</span>
<% end %>
I grabbed the spinner svg from this example at https://flowbite.com/docs/components/spinner/
I referenced this screencast on this Trix Github issue where comments disappear if the user mentioned is a link and is clicked. Required a data turbo false tag to be filtered from trix content divs as well as allowing the data turbo false html content to be an allowed_attributes of action text.
Thank you so much! Was wrestling with this and having a solution with an explanation is pure gold!
But isn't the problem that, when comparing comment.user
to current_user
and current_user
is not available through the renderer??
Interested to know what OuttaSpaceTime was doing to successfully pass the current_user
around. Currently struggling to access it via the comment.rb
callbacks like after_create_commit
.
Same problem here. Did you end up finding any solution to this? Right now I am investigating the use of view_components
gem but seems heavy handed :/
Found that this is harder if using options_from_collection_for_select
so gave up and ended up plucking my main value (in this case name
) from the collection and using options_for_select
then using a lookup on the name
when sending the data back to the controller on submission.
Haha cheers... after reading on :)
Were you able to find a solution here? I am having the same problem with include_blank and any of the other values initially set before the turbo_stream updates the id.
<%= f.select(
:food_id,
options_from_collection_for_select(
@foods,
:id,
:name
),
{
include_blank: true,
required: false
},
{
class: "form-input-group-text-field",
multiple: false,
data: {
filteredforms_target: "foodSelect"
}
}
)
%>
Great video. Instantly love this gem! Had a task running in less than a few minutes which is great. Only problem I can't seem to find info on.
How do I authenticate/authorise this with pundit?
Appears to just set the route to be publicly facing with no documentation on integrating into the gems controllers...
But maybe something to learn, so on Stackoverflow I am reading through https://stackoverflow.com/questions/2881093/override-a-rails-engine-controller-action
I've also done the upgrade from Turbolinks to Turbo and had to make this change. It was the only detail that I could find to store (ie those other two options had to be removed (title
and pagename
or something).
If, like me, you rushed through this video and are now are seeing the error from warden Invalid strategy some_external_strategy
... it could be because you uncommented the entire block in devise.rb
initializer file. I didn't want to post this comment because I might look silly but... in case someone else makes my mistake :D
config.warden do |manager|
manager.failure_app = TurboFailureApp
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
end
and not
config.warden do |manager|
manager.failure_app = TurboFailureApp
manager.intercept_401 = false
manager.default_strategies(scope: :user).unshift :some_external_strategy
end
I can also confirm that creating a ./app/controllers/turbo_controller.rb
instead of adding it to the top of the devise.rb
initializers fixed my undefined local variable or method 'turbo_include_tags'
error.
Posted in How to use Hotwire in Rails Discussion
I'd also like the answer to that :)
To make it as commenters
you'd need to set an alias on the users association on comments. Hope this helps. Sorry I don't have exact code for it.
@post.comments.users
would work if you had your comments has_many: users
association on your comment
model.
Pundit is my preference for this type of functionality. I've followed this tutorial and added the appropriate pundit policies on posts and comments along the way. +1 to pundit :)
I have fixed this issue but it requires quite a few changes. First of all, the cause is that the ajax is only rerendering the partial, which means the nesting value is not being incremented. That much is fairly obvious.
To fix this, I moved the max_nesting
into the Comment
model as a class variable. ie def self.max_nesting 3 end
. I then replace all references as Comment.max_nesting
. You can then move that part of the logic into the comments helper.
The second fix was to take the nesting value for the comment and add it as a field on the Comment model. So you know that the @comment.nesting
value is stored with the comment itself.
It is worth noting I have the paranoia/soft delete function set which I think has reduced my chances of the nesting becoming broken as comments are deleted.
In my comment controller, I am storing the comment nesting value through a Comment model method called set_nesting
. This increments from the parent comment OR sets it to 1.
comments_helper.rb
def reply_to_comment_id(comment, nesting)
nesting = 1 unless nesting.present?
max_nesting = Comment.max_nesting
if max_nesting.blank? || nesting < max_nesting
comment.id
else
comment.parent_id
end
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
@comment = @commentable.comments.new(comment_params)
@comment.nesting = @comment.set_nesting
@comment.user = current_user
if @comment.save
respond_to do |format|
format.html { redirect_to @commentable }
format.js
end
else
redirect_to @commentable, alert: "Something went wrong."
end
end
def destroy
@comment = @commentable.comments.find(params[:id])
@comment.destroy
redirect_to @commentable
end
def restore
@comment = @commentable.comments.with_deleted.find(params[:id])
@comment.restore
redirect_to @commentable
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id)
end
end
comment.rb
class Comment < ApplicationRecord
acts_as_paranoid
belongs_to :user
belongs_to :commentable, polymorphic: true
belongs_to :parent, optional: true, class_name: "Comment"
validates :body, presence: true
validates_length_of :body, maximum: 140
def comments
Comment.with_deleted.where(commentable: commentable, parent_id: id).order(created_at: :asc)
end
def self.max_nesting
3
end
def set_nesting
if self.parent.present? && self.parent.nesting.present?
self.nesting = self.parent.nesting + 1
else
self.nesting = 1
end
end
end
_comment.html.erb
<div class="border-gray-300 border-l p-4 my-4 mt-2 ml-2">
<div class="flex"><%= comment.user.name %> says..</div>
<% if comment.deleted? %>
<div class="border-gray-300 border-l p-2 italic text-gray-500">
<%= simple_format "This comment has since been deleted..." %>
<div class="italic text-gray-500 text-sm">
<div class="flex">
<%= comment.created_at.strftime("%I:%M %p") %> • <%= comment.created_at.strftime("%d %b %y") %> <%= "~" + time_ago_in_words(comment.created_at) + " ago."%>
</div>
</div>
</div>
<% else %>
<div class="border-gray-300 border-l p-2">
<%= simple_format comment.body %>
<div class="italic text-gray-500 text-sm">
<div class="flex">
<%= comment.created_at.strftime("%I:%M %p") %> • <%= comment.created_at.strftime("%d %b %y") %> <%= "~" + time_ago_in_words(comment.created_at) + " ago."%>
</div>
</div>
</div>
<% end %>
<div class="mt-2" data-controller="reply">
<% if policy(comment).create? %>
<%= link_to "Reply", "#", class: "text-green-700", data: { action: "click->reply#show" } %>
<% end %>
<% if policy(comment).destroy? %>
<%= link_to "Delete", comment_path(comment, post_id: comment.commentable), method: :delete, class: "text-red-700", data: { confirm: "Are you sure?" } %>
<% end %>
<% if policy(comment).restore? %>
<%= link_to "Undo", restore_comment_path(comment, post_id: comment.commentable), method: :patch, class: "text-red-700", data: { confirm: "Restore this comment?" } %>
<% end %>
<%= render partial: "comments/form", locals: {
commentable: comment.commentable,
parent_id: reply_to_comment_id(comment, comment.nesting),
method: "post",
class: "hidden",
target: "reply.form"
} %>
</div>
<%= tag.div id: "#{dom_id(comment)}_comments" do %>
<%= render comment.comments, nesting: comment.nesting %>
<% end %>
</div>
create.js.erb
<% if @comment.parent_id? %>
var comments = document.querySelector("#<%= dom_id(@comment.parent) %>_comments")
<% else %>
var comments = document.querySelector("#comments")
<% end %>
comments.insertAdjacentHTML('beforeend', '<%=j render partial: "comments/comment", locals: { comment: @comment, nesting: @comment.nesting }, format: :html %>')
var form = comments.parentElement.querySelector("form")
form.reset()
<% if @comment.parent_id? %>
form.classList.add("hidden")
<% end %>
show.html.erb
<h2 class="title-2">Comments</h2>
<div class="mt-4">
<div id="comments">
<%= render partial: "comments/form", locals: {commentable: @post, method: "post" } %>
</div>
</div>
<%= render @comments %>
</div>
20200901061626_add_nesting_to_comments.rb
class AddNestingToComments < ActiveRecord::Migration[6.0]
def change
add_column :comments, :nesting, :integer
end
end