The easiest way would be to execute the View again in the page preprocess hook.
If you want to avoid executing the View twice the task is getting a bit more tricky. This is because in D8 page elements are not necessarily rendered downstream in the order you expect. The element could have been cached in a previous request or in case the element doesn't qualify for caching it could have automatically been placeholdered and the page has been built without it, postponing the rendering until the cached page is finally delivered.
In Drupal 8 elements often need to send information upstream despite being cached or placeholdered, like cache tags or libraries. The mechanism for this is bubbleable metadata. You can use this for other things, too. See for example Add an attribute to $variables from a Controller
In case of the views results you can try this:
/**
* Implements hook_views_pre_render().
*/
function mymodule_views_pre_render($view) {
if (isset($view) && ($view->id() == 'my_view')) {
$view->element['#attached']['placeholders']['<div id="my-view-results"></div>']['#markup'] =
'<div id="my-view-results">' . $view->total_rows . '</div>';
}
}
Place the empty div container somewhere on the page, for example in the body field of a custom block or directly in page.html.twig:
<div id="my-view-results"></div>
AJAX
When you enable AJAX the code above only works for the first page load. To update the results when the View gets refreshed via AJAX add your own command in a response event subscriber.
Getting the executed View then is straightforward, Views AJAX implementation keeps it in the response, so that you can use it when altering the response, see ViewAjaxResponse::getView.
/src/EventSubscriber/ViewAjaxResponseSubscriber.php
<?php
namespace Drupal\mymodule\EventSubscriber;
use Drupal\views\Ajax\ViewAjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Response subscriber to handle View AJAX responses.
*/
class ViewAjaxResponseSubscriber implements EventSubscriberInterface {
/**
* Adds an AJAX command to ViewAjaxResponse.
*/
public function onResponse(FilterResponseEvent $event) {
$response = $event->getResponse();
if ($response instanceof ViewAjaxResponse) {
$view = $response->getView();
if (isset($view) && ($view->id() == 'my_view')) {
$response->addCommand(new HtmlCommand('#my-view-results', $view->total_rows));
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = ['onResponse'];
return $events;
}
}
mymodule.services.yml
services:
mymodule.view_ajax_response_subscriber:
class: Drupal\mymodule\EventSubscriber\ViewAjaxResponseSubscriber
tags:
- { name: event_subscriber }