comments
comments copied to clipboard
Custom Templates but default JS
Question
Hi, is it possible to use custom templates with craft.comments.fetch() but still use the default JS?
Additional context
Just leaving the setting on for JS doesn't work. I found craft.comments.renderJs(), but it looks like it only works with craft.comments.render()?
You certainly can BYO templates if you like, but you still need to have certain markup to hook up to Comments' JS.
{# Render the `comments.js` file - exclude the inline JS that initializes the instance #}
{{ craft.comments.renderJs(entry.id, {}, false) }}
{# Fetch the variables required for the comments JS #}
{% set jsVariables = craft.comments.getJsVariables(entry.id) %}
{# Minimal markup to get you going (doesn't include everything) #}
<div id="cc-w-{{ entry.id }}">
<div data-role="comments">
{% for comment in craft.comments.fetch().owner(entry).all() %}
<article id="comment-{{ comment.id }}" data-id="{{ comment.id }}" data-site-id="{{ comment.siteId }}" data-role="comment" itemprop="comment" itemscope itemtype="http://schema.org/UserComments">
<div data-role="wrap-content">
<div data-role="content">
<div data-role="body">
<div data-role="message" dir="auto" itemprop="commentText">
<p>{{ comment.comment | nl2br }}</p>
</div>
</div>
</div>
<div data-role="reply"></div>
<div data-role="errors"></div>
<div data-role="notice"></div>
</div>
</article>
{% endfor %}
<article data-role="form">
<form role="form" method="post" accept-charset="UTF-8">
<input type="hidden" name="action" value="comments/comments/save">
<input type="hidden" name="elementId" value="{{ entry.id }}">
<input type="hidden" name="siteId" value="{{ entry.siteId }}">
{{ craft.comments.protect() }}
{{ csrfInput() }}
<textarea name="fields[comment]" placeholder="Add a comment..." required></textarea>
<div data-role="notice"></div>
<button type="submit">{{ 'Post comment' | t('comments') }}</button>
</form>
</article>
</div>
</div>
{# Wait for the document to be ready, then initialise #}
{% js %}
document.addEventListener('DOMContentLoaded', function() {
// Populate the ID and the settings
new Comments.Instance('{{ jsVariables.id }}', {{ jsVariables.settings | json_encode | raw }});
});
{% endjs %}
The HTML above is just a bare-bones example, it doesn't contain everything like avatars, nested comments, replies, etc. That's too much for one file, and why we split them out into multiple includes.
I'd still probably recommend template overrides just because you otherwise have to template everything from scratch. Because while the above works, it's going to load in the Twig for the new comment to be inserted via JS, and it'll use the template you specify. It's for this reason I'd really suggest using template overrides, which allows you to tell Comments which templates to use.
Then, you've got total control over the markup, provided you just add those data attributes (data-role are typically what's used to hook things up).
Thanks. I will try that! I am currently using overrides, but the js wasn't working anymore.
It's working, but not fully. The reason I am using fetch instead of render is so I can load more comments with Sprig.
The JS is only working for the initially loaded comments. Could there something that might be missing on the comments that are loaded later with sprig? The mark up is looking good, and I do not get any errors. Instead of opening the reply form the links just do their # href.
That sounds like it probably won't work with our JS, as it won't work for dynamically loaded items. You essentially need to call Comments.Instance() after every "load more" so that new comments lazy-loaded in are initialised. The Comments' JS doesn't have a convenient per-comment init function, just the overall container to initialize any comments within it.
Alright. Thank you.