Load more button

I have events page and templates/events.php for it:

<div class="page__container">
	<?php snippet('events/events', [
		'type' => 'future',
		'eventsList' => $eventsFuture,
		'eventsListPagination' => $paginationFuture
	]) ?>
	<?php snippet('events/events', [
		'type' => 'past',
		'title' => t('past-events'),
		'eventsList' => $eventsPast,
		'eventsListPagination' => $paginationPast
	]) ?>
</div>

Controller controllers/events.php:

<?php
return function ($kirby) {
	$limit = 5;
	$eventsFuture = $kirby->collection('events-future')->paginate($limit);
	$eventsPast = $kirby->collection('events-past')->paginate($limit);
	return [
		'limit' => $limit,
		'eventsFuture' => $eventsFuture,  
		'eventsPast' => $eventsPast,  
		'paginationFuture' => $eventsFuture->pagination(),  
		'paginationPast' => $eventsPast->pagination(),  
	];
};

Events list snippet:

<ul class="events__list _row-md" data-events="<?= $type ?>" data-page="<?= $eventsListPagination->nextPage() ?>">
<?php foreach($eventsList as $event): ?>
	<?php snippet('events/event', [ 'event' => $event ]) ?>
<?php endforeach ?>
</ul>

<?php if($eventsListPagination->hasNextPage()): ?>
<button
	class="button _more-link _down events__more-link"
	type="button"
	accesskey="m"
>
	<span class="button__text"><?= t('button-show-more') ?> 5</span>
</button>
<?php endif ?>

Controller for JSON — controllers/events-past.json.php:

<?php
return function ($kirby) {
	$limit = 5;
	$events = $kirby->collection('events-past')->paginate($limit);
	$pagination = $events->pagination();
	$more = $pagination->hasNextPage();
	return [
		'events' => $events,
		'pagination' => $pagination,
		'more' => $more,
		'html' => '',
		'json' => [],
	];
};

Template for JSON — templates/events-past.json.php:

<?php
foreach($events as $event) {
	$html .= snippet('events/event', ['event' => $event], true);
}
$json['html'] = $html;
$json['more'] = $more;
echo json_encode($json);

And JS for load more button:

containers.forEach(container => {
	let page = Number(container.getAttribute('data-page'));

	if (!page) {
		return;
	}

	const button = container.nextElementSibling;
	const type = container.getAttribute('data-events');
	const fetchProjects = async () => {
		const url = `${window.location.href.split('#')[0]}-${type}.json/page:${page}`;
		try {
			const response = await fetch(url);
			const { html, more } = await response.json();
			button.hidden = !more;
			container.insertAdjacentHTML('beforeend', html);
			page++;
		} catch (error) {
			console.log('Fetch error: ', error);
		}
	};
	button.addEventListener('click', fetchProjects);
});

I see first 5 items of events list and load more button. When I click the button I get error:

GET http://localhost:8000/en/events-past.json/page:2 404 (Not Found)
Fetch error: SyntaxError: Unexpected token ‘<’, "<!doctype "… is not valid JSON

This template (and the controller) don’t seem to have a standard php template. Why is it called events-past, instead of just events.json.php?

Because I have 2 lists on 1 page (events.php). future (4 items) and past (14 items) events.
image

So I need 2 JSONs.

Then you will have to create them as non-json templates/controllers, i.e. give them a name without the extension, but keep the code as is.

What should I do? Just rename events-future.json.php and events-past.json.php to events-future.php and events-past.php

Now I have controllers:
controllers/events-future.php
controllers/events-past.php
And templates:
templates/events-future.php
templates/events-past.php

But it seems doesn’t create these pages.
http://localhost:8000/en/events-past show me 404 page.
How to generate http://localhost:8000/en/events-past.json with pagination?

In order to have something available at the /events-past url you have to either have a page inside the content folder that is using the correct events-past.txt file (that in turn will make use of the events-past.php controller) or you have to set up a custom route

What should I write in content files? I generate json in template.
If I create empty events-past/events-past.en.txt
I get this page

Let me try to clarify some confusion for you here so that you can figure out what’s the best setup for your situation.

You have an /events page with an events.en.txt file inside it. This means Kirby will attempt to load the events.php template and also the events.php controller.

So far so good.

For the /events-past and /events-future URLs to work, as mentioned before, you either need to have those two pages available inside the content folder or you need routes. You picked option one and created a page.

Now, you have a /events-past folder with an events-past.en.txt inside it. This means Kirby will try load the events-past.php template and events-past.php controller.

You said you want to generate JSON and you decided to use content representation which is not a bad idea. In order for that to work you need a events-past.json.php template but, as per the documentation:

A content representation requires that the standard template with the base name is present. If you want to create a JSON representation project.json.php , make sure that project.php exists.

So, in your case, you need both templates to be present. Now, you can also have a events-past.json.php controller that will be paired with the events-past.json.php template.

So, to answer your question, if I was in your situation and I had to use your setup, I’d use the events-past.json.php template and controller to generate my JSON, I’d leave the events-past.php template empty and i’d use the events-past.php controller to redirect the page if someone were to visit that url for some reason.

Ty for this
Now I get json. But by default it has first 5 items.


I need next pages JSON for fetch it in JS

But when I try to get next page http://localhost:8000/en/events-past.json/page:2, I get 404:

Controller events-past.json.php:

<?php
return function ($kirby) {
	$limit = 5;
	$events = $kirby->collection('events-past')->paginate($limit);
	$pagination = $events->pagination();
	$more = $pagination->hasNextPage();
	return [
		'events' => $events,
		'more' => $more,
		'html' => '',
		'json' => [],
	];
};

Template events-past.json.php:

<?php
foreach($events as $event) {
	$html .= snippet('events/event', ['event' => $event], true);
}
$json['html'] = $html;
$json['more'] = $more;
echo json_encode($json);

I’m honestly not entirely sure why it’s not working for you. Pagination should work normally even with content representation and I just tested this with a plain kit and it is indeed working normally.

Can I debug it somehow to understand where is the problem?

Do you have a copy of your project available somewhere? I might be able to take a quick look at it for you.

Yes. I have it on github GitHub - kalininmax/brosko-mall-dev

Your issue is probably related to the local environment because it’s working just fine on my machine using MAMP Pro so something about your local server is interfering

I use default php and composer settings and start the local server with composer start command :neutral_face:

I remember reading somewhere here on the forum that some people were having issues using the built in server. That said, I can’t help you more than this, at this point this is no longer a Kirby issue I believe and I don’t have much experience with local server configurations.

I’ll try to use MAMP like local servers, hope it’ll work.
Thank you so much for your help :heart:

1 Like

In my opinion, this whole setup could be greatly simplified by using a content representation for the original events.php template/controller, and then to call the content representation with a parameter or query string to separate between past and present. No need to add additional pages which then needs to be redirected etc.

You will still have to sort out your server issue with the built-in server or use another local dev environment.

1 Like