# Load More with AJAX

**URL:** <https://forum.getkirby.com/t/load-more-with-ajax/17949>\
**Category:** Questions\
**Tags:** v3\
**Created:** [April 20, 2020, 4:46am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949 "2020-04-20T04:46:59Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jjh](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/jjh/32/8279_2.png) [@jjh](https://forum.getkirby.com/u/jjh)\
**Post date:** [April 20, 2020, 4:46am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/1 "2020-04-20T04:46:59Z")

</div>

I’ve attempted to follow the K2 [Load more with AJAX](https://k2.getkirby.com/docs/cookbook/ajax-load-more) tutorial but haven’t been able to get it to work. I get the error when I load my project page: Call to undefined method Kirby\Http\Request::ajax()

Is there something I need to be doing differently to get it to work in K3?

Grateful for any help as I have little experience with AJAX.

Thanks!

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [April 20, 2020, 6:05am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/2 "2020-04-20T06:05:08Z")

</div>

There is no replacement for the AJAX check in Kirby 3, see this discussion on GitHub: [https://github.com/getkirby/kirby/issues/1431](https://github.com/getkirby/kirby/issues/1431)

I think the solution would be to use a second controller for the json template.

---

<div class="post-metadata">

**Author:** ![jjh](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/jjh/32/8279_2.png) [@jjh](https://forum.getkirby.com/u/jjh)\
**Post date:** [April 20, 2020, 7:17am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/3 "2020-04-20T07:17:53Z")

</div>

Hmmm I see. I hate to be a pain, but I’m not sure I follow what I’d need to do to get it working? How would a second controller for json template work?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [April 20, 2020, 7:20am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/4 "2020-04-20T07:20:37Z")

</div>

It gets the name of the template, in this example, `projects.json.php`. And you would put in the code from within the ajax check.

---

<div class="post-metadata">

**Author:** ![jjh](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/jjh/32/8279_2.png) [@jjh](https://forum.getkirby.com/u/jjh)\
**Post date:** [April 20, 2020, 8:13am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/5 "2020-04-20T08:13:03Z")

</div>

So I’d create a controller called `projects.json.php` and have it set up like this? (Sorry just a stab in the dark lol)

```
<?php

  if(r::ajax() && get('offset') && get('limit')) {

    $offset = intval(get('offset'));
    $limit = intval(get('limit'));

    $projects = $projects->offset($offset)->limit($limit);

    $more = $count > $offset + $limit;

  } else {

    $offset = 0;
    $limit = 2;
    $projects = $projects->limit($limit);

  }

  return compact('offset', 'limit', 'projects', 'more');

};
```

---

<div class="post-metadata">

**Author:** ![jimbojones](https://avatars.discourse-cdn.com/v4/letter/j/e5b9ba/32.png) [@jimbojones](https://forum.getkirby.com/u/jimbojones)\
**Post date:** [April 21, 2020, 2:52am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/6 "2020-04-21T02:52:48Z")

</div>

I’m also looking to implement load more using Ajax and am having the same issue. How would the two controllers work?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [April 21, 2020, 6:05am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/7 "2020-04-21T06:05:43Z")

</div>

Your controller is not complete, have a look at the docs for the [syntax of a controller](https://getkirby.com/docs/guide/templates/controllers). Also, the if (r::ajax) statement has to disappear from the if statememt. But you have to define the `$projects` and `$more`variables.

---

<div class="post-metadata">

**Author:** ![jamiehunter](https://avatars.discourse-cdn.com/v4/letter/j/d07c76/32.png) [@jamiehunter](https://forum.getkirby.com/u/jamiehunter)\
**Post date:** [May 12, 2020, 9:53am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/8 "2020-05-12T09:53:17Z")

</div>

@jjh Did you work out a functioning controller for this? I’m also struggling with implementing this tutorial with K3.

---

<div class="post-metadata">

**Author:** ![jamiehunter](https://avatars.discourse-cdn.com/v4/letter/j/d07c76/32.png) [@jamiehunter](https://forum.getkirby.com/u/jamiehunter)\
**Post date:** [May 19, 2020, 2:38pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/9 "2020-05-19T14:38:52Z")

</div>

Hi @texnixe

I’m struggling to get my head around this: is this now not possible in Kirby because of the deprecated `request`? Or is there a simple workaround with an additional controller that I’m missing? Thanks!

---

<div class="post-metadata">

**Author:** ![pixelijn](https://avatars.discourse-cdn.com/v4/letter/p/7ab992/32.png) [@pixelijn](https://forum.getkirby.com/u/pixelijn)\
**Post date:** [May 19, 2020, 3:18pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/10 "2020-05-19T15:18:55Z")

</div>

The solution is to use a content representation that is called from your AJAX request instead of using the same template/controller for both.

---

<div class="post-metadata">

**Author:** ![jamiehunter](https://avatars.discourse-cdn.com/v4/letter/j/d07c76/32.png) [@jamiehunter](https://forum.getkirby.com/u/jamiehunter)\
**Post date:** [May 19, 2020, 5:57pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/11 "2020-05-19T17:57:13Z")

</div>

I’m struggling to understand what that means. Any examples you can share?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 19, 2020, 6:07pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/12 "2020-05-19T18:07:55Z")

</div>

Before we had content representations, we had to use the standard PHP template to handle both the standard HTML request and the requests coming from an AJAX call, therefore the need for the if statement with the `if(r::ajax()` condition.

But since Kirby 2.ican’trememberthe version, we have [content representations](https://getkirby.com/docs/guide/templates/content-representations), that allow you to output the content in different formats.

While the example Load more recipe still uses the if statement, you can in fact handle the logic in the if statement in a separate controller for the representation instead.

So you would end up with 4 files instead of three:

- projects.php template
- projects.json.php template
- projects.php controller
- projects.json.php controller

The logic you need for the `projects.json.php` template needs to go into the `projects.json.php` controller.

---

<div class="post-metadata">

**Author:** ![jamiehunter](https://avatars.discourse-cdn.com/v4/letter/j/d07c76/32.png) [@jamiehunter](https://forum.getkirby.com/u/jamiehunter)\
**Post date:** [May 20, 2020, 11:10am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/14 "2020-05-20T11:10:14Z")

</div>

I got this working, and actually understand why/how! Thanks!

---

<div class="post-metadata">

**Author:** ![jjh](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/jjh/32/8279_2.png) [@jjh](https://forum.getkirby.com/u/jjh)\
**Post date:** [May 26, 2020, 4:12am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/15 "2020-05-26T04:12:02Z")

</div>

Hey @jamiehunter, could you share how you implemented with me?

---

<div class="post-metadata">

**Author:** ![jamiehunter](https://avatars.discourse-cdn.com/v4/letter/j/d07c76/32.png) [@jamiehunter](https://forum.getkirby.com/u/jamiehunter)\
**Post date:** [May 28, 2020, 1:43pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/16 "2020-05-28T13:43:18Z")

</div>

Sure. I’ll base it on the K2 example for you:

**/site/templates/projects.php**

```
<ul class="projects" data-page="<?= $page->url() ?>" data-limit="<?= $limit ?>">

  <?php foreach($projects as $project): ?>
    <?php snippet('project', compact('project')) ?>
  <?php endforeach ?>

</ul>
<button class="load-more">Load more</button>

```

**/site/snippets/project.php**

```
<li class="project">
  <a href="<?= $project->url() ?>" class="project-link">
    <?php if($image = $project->image()): ?>
    <img src="<?= $image->url() ?>" alt="<?= $image->alt_text()->html() ?>" />
    <?php endif ?>
    <div class="project-caption">
      <h2 class="project-title"><?= $project->title()->html() ?></h2>
    </div>
  </a>
</li>

```

**/site/controllers/projects.php**

```
<?php

return function($site, $pages, $page) {

  $projects = $page->children()->visible();
  $count = $projects->count();
  $more = false;

  $offset = 0;
  $limit = 2;
  $projects = $projects->limit($limit);

  return compact('offset', 'limit', 'projects', 'more');

};

```

**/site/templates/projects.json.php**

```
<?php

$html = '';

$projects = $page->children()->visible();
$count = $projects->count();

$offset = intval(get('offset'));
$limit = intval(get('limit'));

$projects = $projects->offset($offset)->limit($limit);
$more = $count > $offset + $limit;

foreach($projects as $project) {

  $html .= snippet('project', compact('project'), true);

}

$data['html'] = $html;
$data['more'] = $more;

echo json_encode($data);

```

**/site/controllers/projects.json.php**

```
<?php

return function ($kirby, $site, $pages, $page) {
  
  $projects = $page->children()->visible();
  $count = $projects->count();

  $offset = intval(get('offset'));
  $limit = intval(get('limit'));

  $projects = $projects->offset($offset)->limit($limit);

  $more = $count > $offset + $limit;

  return [
      compact('offset', 'limit', 'projects', 'more')
    ];
};

```

**JS**

```
$(function () {
  var element = $(".projects");
  var url = element.data("page") + ".json";
  var limit = parseInt(element.data("limit"));
  var offset = limit;

  $(".load-more").on("click", function (e) {
    $.get(url, { limit: limit, offset: offset }, function (data) {
      if (data.more === false) {
        $(".load-more").hide();
      }

      element.children().last().after(data.html);

      offset += limit;
    });
  });
});

```

This should work. Let me know if you have any problems!

---

<div class="post-metadata">

**Author:** ![jjh](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/jjh/32/8279_2.png) [@jjh](https://forum.getkirby.com/u/jjh)\
**Post date:** [June 24, 2020, 4:59am UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/17 "2020-06-24T04:59:11Z")

</div>

Thank you so much for this! Got it working! The only issue I noticed was the use of visible() instead of listed()

---

<div class="post-metadata">

**Author:** ![jamiehunter](https://avatars.discourse-cdn.com/v4/letter/j/d07c76/32.png) [@jamiehunter](https://forum.getkirby.com/u/jamiehunter)\
**Post date:** [June 24, 2020, 12:30pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/18 "2020-06-24T12:30:51Z")

</div>

Ah yes, a hangover from the old cookbook. I use a collection to get my articles.

---

<div class="post-metadata">

**Author:** ![iam\_timm](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/iam_timm/32/4728_2.png) [@iam\_timm](https://forum.getkirby.com/u/iam_timm)\
**Post date:** [November 10, 2020, 2:50pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/19 "2020-11-10T14:50:56Z")

</div>

Hi @jamiehunter I’ve been trying to replicate your solution but I can’t seem to get any of it to work. Any thoughts on how to debug?

**/site/templates/interviews.php**

```
<?php foreach($interviews as $interview): ?>
  <?php snippet('single-interview', [
    'interview' => $interview,
    'size' => 'default'
  ]) ?>
<?php endforeach ?>

<button class="load-more">Load more</button>

```

**/site/controllers/interviews.php**

```
return function ($page, $pages) {

  $identifier = $page->template(); // uniquely identiy page by type

  $interviews = $pages->find('interviews')->children()->flip()->listed();
  $count = $interviews->count();
  $more = false;

  $offset = 0;
  $limit = 2;
  $interviews = $interviews->limit($limit);

  return compact('identifier', 'offset', 'limit', 'interviews', 'more');

};  

```

**/site/templates/interviews.json.php**

```
$html = '';

$interviews = $pages->find('interviews')->children()->flip()->listed();
$count = $interviews->count();
$offset = intval(get('offset'));
$limit = intval(get('limit'));

$interviews = $interviews->offset($offset)->limit($limit);
$more = $count > $offset + $limit;

foreach($interviews as $interview) {

  $html .= snippet('
    single-interview',
    [
      'interview' => $interview,
      'size' => 'default'
    ],
    true
  );

}

$data['html'] = $html;
$data['more'] = $more;

echo json_encode($data);

```

**/site/controllers/interviews.json.php**

```
return function ($kirby, $site, $pages, $page) {
  
  $interviews = $pages->find('interviews')->children()->flip()->listed();
  $count = $interviews->count();

  $offset = intval(get('offset'));
  $limit = intval(get('limit'));

  $interviews = $interviews->offset($offset)->limit($limit);

  $more = $count > $offset + $limit;

  return [
      compact('offset', 'limit', 'interviews', 'more')
    ];
};

```

**assets/js/interviews.js**

```
$(function() {
  const element = $('.interviews');
  const url = `${element.data('page')}.json`;
  const limit = parseInt(element.data('limit'));
  let offset = limit;

  $('.load-more').on('click', function(e) {
    console.log(url);
    $.get(url, { limit, offset }, function(data) {
      if (data.more === false) {
        $('.load-more').hide();
      }

      element
        .children()
        .last()
        .after(data.html);

      offset += limit;
    });
  });
});
```

---

<div class="post-metadata">

**Author:** ![pixelijn](https://avatars.discourse-cdn.com/v4/letter/p/7ab992/32.png) [@pixelijn](https://forum.getkirby.com/u/pixelijn)\
**Post date:** [November 10, 2020, 3:32pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/20 "2020-11-10T15:32:02Z")

</div>

It’s definitely not correct to have the logic in the controller _and_ in the template?

---

<div class="post-metadata">

**Author:** ![iam\_timm](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/iam_timm/32/4728_2.png) [@iam\_timm](https://forum.getkirby.com/u/iam_timm)\
**Post date:** [November 10, 2020, 3:44pm UTC](https://forum.getkirby.com/t/load-more-with-ajax/17949/21 "2020-11-10T15:44:34Z")

</div>

Yeah, totally makes sense.

If I keep the logic in the controller, the.json page throws an error

`Undefined variable: interviews`

when its in the template it runs fine but the json file is empty.

[Next page](https://forum.getkirby.com/t/load-more-with-ajax/17949.md?page=2)
