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. 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. 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. 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. 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 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. Commented Jun 19, 2017 at 3:16
  • I thought ->safe_value worked, but it's actually not printing anything. 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. 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. 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. Commented Dec 12, 2018 at 22:49

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.