6

I created a custom template for my programmatically created block.

I defined a variable for the template called test

so in my render array I have

'#test' => $node->get('body')->value

Problem is when I print {{ test }} in my twig template, it literally prints the html rather than rendering it. Ex: <p> My node body </p>

I've also have tried:

'#test' => $node

Then on twig {{ test.body.value }}

but same result, the html does not get rendered.

The only way I found to make render is by doing {{ test.body.value|raw }} but raw filter should never be used on user entered data.

How can I make Drupal stop escaping the html safely?

2 Answers 2

19

Build a render element processed_text and set the text format from the body field, so that the right text filters are applied before you print the field with {{ body }}:

If you have the node in #test like in your second example:

'#test' => $node

then you can build the render element in twig:

{% set body = {
  '#type':   'processed_text',
  '#text':    test.body.value,
  '#format':  test.body.format,
} %}

{{ body }}

This is the preferred method, see the documentation of check_markup():

Note: this function should only be used when filtering text for use elsewhere than on a rendered HTML page. If this is part of a HTML page, then a renderable array with a #type 'processed_text' element should be used instead of this, because that will allow cacheability metadata to be set and bubbled up and attachments to be associated (assets, placeholders, etc.). In other words: if you are presenting the filtered text in a HTML page, the only way this will be presented correctly, is by using the 'processed_text' element.

https://api.drupal.org/api/drupal/core%21modules%21filter%21filter.module/function/check_markup/8.2.x


Render element in php:

'#test' => [
  '#type' => 'processed_text',
  '#text' => $node->body->value,
  '#format' => $node->body->format,
],

and print in twig:

{{ test }}
8
  • Cool, I didn't know you could do stuff like this on twig. I'm gonna go with @Japan's answer just because it's a little more bullet proof since someone else is going to be doing the theming. I know I am splitting hairs here.
    – No Sssweat
    Commented Jun 19, 2017 at 8:20
  • You can build the render element in php as well, but you really should use the render element, see the documentation.
    – 4uk4
    Commented Jun 19, 2017 at 8:22
  • you really should use the render element fantabulous, one slight problem, my actual scenario #test is an array of node objects (didn't mention this in the Q to keep it simple). Not sure how I am going to make the twig looping work with the Render element in php approach. Might not be possible.
    – No Sssweat
    Commented Jun 19, 2017 at 8:59
  • 1
    Yes, put an array in #test with multiple processed_text render elements, all array keys that don't start with a '#' will be considered as render children, also the numbers of an indexed array. You don't need a loop, twig renders all render children it finds in {{ test }} automatically.
    – 4uk4
    Commented Jun 19, 2017 at 9:08
  • 1
    btw. you need to add cache tags from the nodes to the render array if you want that this gets invalidated when a node is modified, or create a view mode for the content type to output the body fields, this is best for caching and performance
    – 4uk4
    Commented Jun 19, 2017 at 9:20
4

I believe you should use:

'#test' => check_markup($node->get('body')->value, $node->get('body')->format);

At which point the data will be safely escaped, and therefore this is safe:

{{ test.body.value|raw }}
10
  • Thanks, I am surprised that nothing in D8 Core uses the ->safe_value, but reading this made me feel good about it.
    – No Sssweat
    Commented Jun 19, 2017 at 3:16
  • I thought ->safe_value worked, but it's actually not printing anything.
    – No Sssweat
    Commented Jun 19, 2017 at 4:12
  • 1
    I edited my code to work with that. I thought safe_value existed in D8, but maybe I was just imagining.
    – Jaypan
    Commented Jun 19, 2017 at 7:06
  • 1
    I gave 4k4's answer an upvote, as it looks to be a better way to go about it.
    – Jaypan
    Commented Jun 19, 2017 at 8:25
  • 1
    If it helps anyone, I was using check_markup to render a field containing a token that outputs a webform, and it wasn't attaching the JS libraries. It was only when I switched to 4k4's approach that it started working. Lost a morning to that one.
    – Geat
    Commented Dec 12, 2018 at 22:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.