How to create a "read more" button using AJAX in a module?
That tutorial won't help you. That's for more advanced stuff.

I would not use a form, but a simple link with an ajax response.

But if you want to use a form you need to create a proper Drupal form. Too much to write here to explain that so just take a look here https://www.drupal.org/node/2117411

Your buildForm should be something like

/**
 * {@inheritdoc}
 */
public function buildForm(array $form, FormStateInterface $form_state) {
  // The default value for working with non JS should be taken from the route and adding 1.
  $form['page_number'] = array(
    '#type' =&gt; 'hidden',
    '#default_value' =&gt; 0,
  );
  $form['my_stuff'] = array(
    '#type' =&gt; 'value',
    '#value' =&gt; [],
  );
  $form['actions']['#type'] = 'actions';
  $form['actions']['submit'] = array(
    '#type' =&gt; 'submit',
    '#value' =&gt; $this-&gt;t('Read more'),
    '#button_type' =&gt; 'primary',
    '#ajax' =&gt; array(
      'callback' =&gt; 'Drupal\example\Controller\MyController::myFormSubmit',
    ),
  );
  return $form;
}


You can call this form in your page with (changing accordingly):

$form = \Drupal::formBuilder()->getForm('Drupal\example\Form\ExampleForm');

You do all the stuff you want in your submitForm, and not in the callback if you want the form to work without JavaScript. If you don't care you can do in the ajax callback. I will assume you do it in the submitForm.

/**
 * {@inheritdoc}
 */
public function submitForm(array $form, FormStateInterface $form_state) {
  $page = $form_state-&gt;getValue('page_number');
  $stuff = getStuffFromDatabase($page);
  $result = array();
  foreach($stuff as $key =&gt; $item) {
    $tmp = new \stdClass();
    $tmp-&gt;id = $item-&gt;id;
    $tmp-&gt;title = $item-&gt;title;
    $tmp-&gt;description = $item-&gt;description;
    $tmp-&gt;image = $item-&gt;image;
    $result[] = $tmp;
  }
  $page++;
  // Here for non JS you probably would just redirect to the proper page
  $form_state-&gt;setValue('page_number', $page);
  $form_state-&gt;setValue('my_stuff', $result);
}


Then in your MyController::myFormSubmit

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AfterCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;

public static function myFormSubmit(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $selector = $form_state-&gt;getValue('my_stuff');

  // Here you build the render array based on the output you want on the page
  $build = [
    '#prefix' =&gt; '&lt;div&gt;',
    '#suffix' =&gt; '&lt;/div&gt;',
  ];
  // The selector is as if you did this in jQuery
  $response-&gt;addCommand(new AfterCommand(
    '.some-selector',
    $build
  ));
  $response-&gt;addCommand(new HtmlCommand(
    '.some-selector-that-wraps-the-form',
    $form
  ));
  return $response;
}


That's the gist of it. It should put you on the right track.

If you want to use a simple link. You need your initial function to take that into account and send what you have now for non JS and a response as in the ajax submit callback when it's called through JS.

Note that if you add a class "use-ajax" to a link and write the link as "/some/path/nojs" Drapal.Ajax will change that to "/some/path/ajax" so its easy to know if the called was made through Ajax or not.