Simply put, I’m polishing off the chat app and I would like to pull the scroll bar down when new items are added to the chat. Does anyone know how to fire a JS function after a turbo stream channel transmission concludes? I’m simply trying to scroll down the container with id conversation_comments after a message is added to the page.
<%= turbo_stream_from @conversation %>
<div class="chat-room" id="chat-room-container">
<%= turbo_frame_tag "conversation" do %>
.................
<% end %>
<div class="convo-comments-container-<%= @conversation.id %>" id="conversation_comments">
<%= render @conversation.conversation_comments %>
</div>
</div>
<div class="chat-box">
<%= turbo_frame_tag 'new_conversation_conversation_comment', src: new_conversation_conversation_comment_path(@conversation), target: "_top" %>
</div>
<script>
$('#conversation_comments').animate({scrollTop: 20000000}, "slow");
const msgs = document.getElementById('conversation_comments');
function getMessages() {
// Prior to getting your messages.
shouldScroll = msgs.scrollTop + msgs.clientHeight === msgs.scrollHeight;
// After getting your messages.
if (!shouldScroll) {
scrollToBottom();
}
}
function scrollToBottom() {
msgs.scrollTop = msgs.scrollHeight;
}
scrollToBottom();
// setInterval(getMessages, 2000);
document.addEventListener("turbo:render", function (){
getMessages();
});
</script>
I’m using the rails version of turbo & never had time with stimulus before now. Maybe you can help me a little bit more. Since rails generates views - the way my system looks a tad bit unique compared to your example.
yeah so if i’m understanding correctly, it looks like you need to hook up your “conversation comments” to the Stimulus controller, rather than the conversation itself. Every time one of those comments is added to the DOM, the connect method will be called and thus you’ll get scrolled to the bottom.
If I use the data-target attribute on a comment itself – can I track when its being inserted into the container using the conversation_controller.js connect method?
yeah, connect gets called when you add something to the DOM. So when your turbo stream appends your comment, connect gets called, so long as it’s on the comment
Not sure Turbo has the events you want. The best approach here does seem to be to rely on the connect method being called on the comments that are added. A la:
Are you using stimulus_include_tags in Rails? That’s what hotwire:install generates. If so, maybe it’s an issue with the dash naming? What if you had a comment_controller.js and did data-controller="comment"?
Trying to solve the same problem. Is there a way to get Turbo to reload a parent element (disconnect then reconnect) whenever a new child element is added? Something similar to below? If so, I think I can solve the issue.
<div class="is-parent" > //Disconnect then reconnect this from the DOM
<%= render @conversation.comments %> //Whenever a new child element is added
</div>
On the other hand, if there’s an alternative to the success Lifecycle callback in Stimulus Reflex, maybe that could be an option.
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="new-message-event-dispatcher"
export default class extends Controller {
connect() {
this.dispatch("new_message", {})
}
}
and finally, this is broadcasted from the model
= turbo_stream.append "messages-container-chat-channel-#{chat_message.chat_channel_id}" do
div data-controller="new-message-event-dispatcher"
= render ChatChannels::ChatMessagesComponent.new(chat_message: chat_message)
Maybe this could be improved, but it works for me so far
The key I think is the connection between controllers taking advantage using dispatch