2

What the proper method of outputting a page from your module without any additional theming beyond your template, for example to make a page that is good for printing?

I've tried a few different methods based on Drupal 7 versions. The only one to work being to render the build array and the kill further PHP loading, however this doesn't feel right. In Drupal 7, I could set up a 'delivery callback' in the hook_menu() - but I can't seem to figure out how to replicate that behaviour now.

my_module.routing.yml:

my_module.user_timesheets:
  path: '/my_module/timesheets/{weekOf}/{uid}'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyModuleAdminController::timesheets'
    _title: 'My Module Timesheets'
  requirements:
    _permission: 'admin'

my_module.module:

  $theme['timesheets'] = [
    'render element' => 'build',
    'template' => 'timesheets',
  ];

MyModuleAdminController.timesheets:

public function timesheets($weekOf, $uid = null) {
    $build['#theme'] = 'timesheets';

    ...code building values...

    $build['#staff'] = $staff;

    $build['#title'] = 'Timesheets - ' . $weekOf . ' to ' . $this->dateService->getWeekStartDate($weekOf);
    $build['#attached']['library'][] = 'my_module/timesheets';
    //print render($build);
    //die();
    return $build;
  }

my_module.libraries.yml:

timesheets:
  css:
    theme:
      lib/timesheets.css: {}

Edit: To over-ride something like page.html.twig in your module, you make a template called page--my_module--html.twig and then declare it in your modules my_module_theme() function.

/**
 * Implements hook_theme().
 */
function my_module_theme() {
  $theme['page__my_module__timesheets'] = [
    'template' => 'page--my_module--timesheets',
  ];

   return $theme;
}
6

The controller does not have to return a render array, it can also provide a symphony response, for example:

use Symfony\Component\HttpFoundation\Response;

$response = new Response();
$response->setContent('<h1>Hello world</h1>');
//$response->headers->set('Content-Type', 'text/xml');
return $response;

This will bypass the Drupal rendering and theming.

Edit:

You can use two approaches to replace your Drupal 7 code:

1) Override page.html.twig and html.html.twig, remove anything out of this templates and put only that in what you want.

2) Render the HTML like you are doing it now in Drupal 7 and use a Symfony response.

Rendering in Drupal 8:

use Symfony\Component\HttpFoundation\Response;

$output = \Drupal::service('renderer')->renderRoot($build);

$response = new Response();
$response->setContent($output);

return $response;
|improve this answer|||||
  • Using the symphony response would mean I can't leverage the render array features, such as templating - no? I'm trying to migrate a module, and it the Drupal 7 version the template that is being loaded is an entire html page using a delivery callback and printing with drupal_render(). So I'm trying to replicate that similar functionality. – EmGee Nov 25 '15 at 16:58
  • Please see the edit in my answer. – 4k4 Nov 25 '15 at 17:16
  • Thanks for all the help, 4k4. The second method seems to be almost there, but for some reason the RendererInterface doesn't seem to include my css library. I've edited my code slightly above to show my library adding code. – EmGee Nov 25 '15 at 17:30
  • With method 2) you must do all things yourself, like add stylesheets to the html head. I think method 1) is the way to go for drupal 8. You can for example put a nearly empty page--my_module--timesheets.html.twig in your theme. There you can remove anything like menus and footer, so that only the main content remains. – 4k4 Nov 25 '15 at 17:48
  • You say in my theme, but can it be done from the module as well? When I put page--my_module--timesheets.html.twig in my-module/templates/ it doesn't seem to override. – EmGee Nov 25 '15 at 17:54
4

If you utilize the html_response.attachments_processor and renderer services along with BareHtmlPageRenderer you can still get libraries to load in the head tag of an otherwise un-templated page.

use Drupal\Core\Render\BareHtmlPageRenderer;

$attachments = \Drupal::service('html_response.attachments_processor');
$renderer = \Drupal::service('renderer');

$bareHtmlPageRenderer = new BareHtmlPageRenderer($renderer, $attachments);

$response = $bareHtmlPageRenderer->renderBarePage($build, 'Page Title', 'markup');
return $response;
|improve this answer|||||
  • This is exactly what I was looking for. I was unable to load attached libs before. But I can now using your advice! Thanks! – InspiredCoder Jun 24 '18 at 16:05
4

I do not have enough reputation to add a comment on a reply, but the anwser by @Arosboro is almost complete, except the "build variable is in the wrong spot. It must be the last variable, like so:

return $bareHtmlPageRenderer->renderBarePage([], 'Address', 'checkout_address', $build);

This way the twig variables I placed in $build were rendered correctly.

My full code:

$build = [
  '#theme' => 'checkout_address',
  '#content' => [],
  '#edit' => FALSE,
];

...

$attachments = \Drupal::service('html_response.attachments_processor');
$bareHtmlPageRenderer = new BareHtmlPageRenderer($this->renderer, $attachments);

return $bareHtmlPageRenderer->renderBarePage([], 'Address', 'checkout_address', $build);

And the template:

{{ content }}
{% if edit == true %}
    <button class="btn-edit-address">Edit Address</button>
{% endif %}

This is how I got it to work.

|improve this answer|||||

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.