0

I'm simply looking to onClick load an entries content via Ajax. Thought I see a lot of mixed answers here on SO, some referring to Element API, Creating a Plugin and GraphQL. Is there a simple way to just get an entries properties on click e.g. title?

2

I can think of a couple of simple ways to output an entry's properties on a click event. If there aren't too many of them, you could output the properties that you want as data attributes on the link/button element in your Twig template, then access them from a JS click event handler:

{# Twig template #}
<a href="someUrl"
    class="myLink"
    data-title="{{ entry.title }}" 
    data-custom-property="{{ entry.customProperty }}">
    Link text
</a>

// JavaScript code
const myLink = document.querySelector('a.myLink');
myLink.addEventListener('click', function() {
    // Get properties
    const title = myLink.getAttribute('data-title');
    const customProperty = myLink.getAttribute('data-custom-property');
});

That's probably the simplest method. If you want to access a lot of properties, you could output them into a JavaScript global variable in your Twig template and access them that way, but that can be messy and pollute the global JS scope.

If you want to get more advanced, the Element API plugin is the next step. This allows you to create endpoints that you can access via XHR/fetch requests, which return JSON data that you specify. See here for an example of how the Element API works.

Using the Element API, your click event handler could fetch the entry's JSON data and output it dynamically as needed. You'd have something like this in your config/element-api.php config file:

<?php

use craft\elements\Entry;

return [
    'endpoints' => [
        'api/entries/all.json' => function() {
            return [
                'elementType' => Entry::class,
                'criteria' => ['section' => 'articles'],
                'transformer' => function(Entry $entry) {
                    return [
                        'title' => $entry->title,
                        'url' => $entry->url,
                        'customProperty' => $entry->customProperty,
                    ];
                },
            ];
        },
        'api/entries/<entryId:\d+>.json' => function($entryId) {
            return [
                'elementType' => Entry::class,
                'criteria' => ['id' => $entryId],
                'one' => true,
                'transformer' => function(Entry $entry) {
                    return [
                        'title' => $entry->title,
                        'url' => $entry->url,
                        'customProperty' => $entry->customProperty,
                    ];
                },
            ];
        },
    ]
];

Accessing https://my-site-url/api/entries/all.json would return a JSON object with an array of all the entries, and accessing https://my-site-url/api/entries/123456.json would return a JSON object with the properties for the Entry with an ID of 123456.

2

You don't need the Element API, or a custom plugin, or GraphQL, or Sprig, for this. Simply set up a template and then call it via ajax. The template can return a chunk of html and use POST or GET parameters to decide what it needs to fetch. You don't mention if you're using a JS framework, but for simplicity here's a jQuery way using load:

/// main template:

<button type="button" data-url="/ajax-load-entry?id=123" data-element-id="123" class="js-ajaxLoad">Load Entry #123</button>
<div class="ajax-result-container-for-123"></div>

<script>
    $('.js-ajaxLoad').click(function(){
        var url = $(this).data('url');
        var elementId = $(this).data('elementId');
        $('.ajax-result-container-for-' + elementId).load(url);
    });
</script>

/// templates/ajax-load-entry.twig template:

{% if craft.app.request.getParam('id') %}
    {% set entry = craft.entries.id(craft.app.request.getParam('id')).one() %}
    {% if entry|length %}
        <h1>{{ entry.title }}</h1>
        {{ entry.body ?? null }}
    {% endif %}
{% endif %}

I appreciate jQuery's not very fashionable these days, but the same concept could very easily be translated into a native fetch call if you don't need IE11 support (or if you're transpiling). Something like this should work in native JS:

<script>
    document.querySelector('.js-ajaxLoad').addEventListener('click', function(){
        let url = this.dataset.url;
        let elementId = this.dataset.elementId;
        fetch(url).then(function(response) {
            return response.text();
        }).then(function (html) {
            document.querySelector('.ajax-result-container-for-' + elementId).innerHTML = html;
        });
    });
</script>
1
  • If I could put both as answers - I would. This is a great example of utilising Craft's own power.
    – JMKelley
    Mar 5 at 11:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.