XF 2.0 Using AJAX with the post macro

AndrewSimm

Well-known member
I am working on a news mod for my site, where I promote a thread to the main page. Once on the main page you click a link to view the news (thread). The news is the first post in a thread (my mod won't let you promote any others). Under this post I am using the post macro to render out the other post (comments). Everything works fine, except when I reply I have to refresh the page to see. I just need to figure out how to get AJAX working with this.

HTML:
<xf:if is="$canInlineMod">
    <xf:js src="xf/inline_mod.js" min="1" />
</xf:if>
<xf:foreach loop="$posts" value="$post">
    <div class="block post-block" data-xf-init="{{ $canInlineMod ? 'inline-mod' : '' }}" data-type="post" data-href="{{ link('inline-mod') }}">
        <div class="block-container">
            <div class="block-body">
                <xf:macro template="post_macros" name="post" arg-post="{$post}" arg-thread="{$thread}" />
            </div>
        </div>
    </div>
</xf:foreach>
<xf:if is="$thread.canReply()">
    <xf:form action="{{ link('threads/add-reply', $thread) }}"
        ajax="true"
        draft="{{ link('threads/draft', $thread) }}"
        class="block js-quickReply"
        data-xf-init="attachment-manager quick-reply{{ $xf.visitor.isShownCaptcha() ? ' guest-captcha' : '' }}"
        data-message-container="< :prev | .js-replyNewMessageContainer"
        data-preview-url="{{ link('threads/reply-preview', $thread, {'quick_reply': 1}) }}">

        <xf:js src="xf/message.js" min="1" />
        <xf:set var="$lastPost" value="{$posts|last}" />

        <div class="block-container">
            <div class="block-body">
                <xf:macro template="quick_reply_macros" name="body"
                    arg-message="{$thread.draft_reply.message}"
                    arg-attachmentData="{$attachmentData}"
                    arg-forceHash="{$thread.draft_reply.attachment_hash}"
                    arg-messageSelector=".js-post"
                    arg-multiQuoteHref="{{ link('threads/multi-quote', $thread) }}"
                    arg-multiQuoteStorageKey="multiQuoteThread"
                    arg-lastDate="{$lastPost.post_date}"
                    arg-lastKnownDate="{$thread.last_post_date}" />
            </div>
        </div>
    </xf:form>
</xf:if>
 
You may want to look into the quick reply JS to understand what the configuration means. But specifically:
Code:
data-message-container="< :prev | .js-replyNewMessageContainer"
That defines where your message is being inserted. That selector won't match anything in your template.
 
You may want to look into the quick reply JS to understand what the configuration means. But specifically:
Code:
data-message-container="< :prev | .js-replyNewMessageContainer"
That defines where your message is being inserted. That selector won't match anything in your template.

ahh, I see. Thank you, sir! The macro made it super easy, what a great concept.
 
You may want to look into the quick reply JS to understand what the configuration means. But specifically:
Code:
data-message-container="< :prev | .js-replyNewMessageContainer"
That defines where your message is being inserted. That selector won't match anything in your template.
What syntax does the data-message-container accept? I get the intent behind the class selector (.js-replyNew...) but unsure of the relationship with < :prev ?
 
Last edited:
It is passed through XF.findRelativeIf() to $.findExtended(), which are functions that I think deserve proper documentation given their prevalence. I haven't found the time to submit a PR for consideration, but if anyone would like to use this information to create one, please feel free. Essentially XF.findRelativeIf supports a comprehensive selector syntax, with $.findExtended() being a custom jQuery plugin that provides the ability to find elements relative to the base element besides children (that is, parents and siblings and their descendants).

XF.findRelativeIf() looks at the first character of the passed selector:

If it is a |, then it is stripped from the selector and the selector is passed to find() (| is basically synonymous with "find relative to the current base element").​
If it is a >, then the selector is simply passed to find() as-is (since find() already supports selecting immediate children and their descendants).​
If it is a <, then the selector is passed to findExtended(), which is a custom jQuery plugin that does the heavy lifting for finding parents and siblings and their descendants by way of changing the base element prior to running find().​
If it is none of |, >, or <, then it is passed as an ordinary jQuery selector ($(...)).​
$.findExtended() allows for using :up :next and :prev to select parents and siblings and their descendants. You can optionally provide numbers to traverse more than once (:up(2) will select the grandparent), chain them together (:up(2) :prev will select the previous sibling of the grandparent), and append regular jQuery selectors to select the closest parents of these elements (:up(2) :prev div will select the closest <div> parent of the previous sibling of the grandparent). Finally, you can append a | and an additional selector to select descendants of these elements (:up(2) :prev div | img will select <img> descendants of the closest <div> parent of the previous sibling of the grandparent).

In this case, < :prev | .js-replyNewMessageContainer will select children of the previous sibling element with the class js-replyNewMessageContainer (relative to the initial base element, which would be the form in the code above).

Happy to clarify/correct anything if needed, much of this was taken from notes I wrote a while back :)
 
Last edited:
Top Bottom