6

I have some list items in drupal 8 which created by item_list template.I am sending some <a> tagged data to the item_list template. My idea was to get the output as <a href="/this-is-new.html">This is new page</a>.

But it returns the output as <li>&lt;a href="/this-is-new.html"&gt;This is new page&lt;/a&gt;</li>.

I need to print this in a twig template. But with the output I had it renders as a string in the template not as a html anchor tag. I needed to view a link to another page in template. But all I am getting is a string with the <a href="/this-is-new.html">This is new page</a>. What should I do?

7 Answers 7

6

NOTE: this should be avoided as it is considered bad practice as mentioned in the comments, leaving here as an example to avoided

Inside your twig template try this

  {{ var|raw }}

where var is the variable being printed.

For more information here

9
  • 1
    Using the |raw filter should be avoided for security reasons. For a better method to get the raw value see How do I get the raw field value in a template?
    – No Sssweat
    Jan 25, 2018 at 5:42
  • 1
    Sorry for the down vote, but I consider using the |raw filter bad practice. You event mentioned it in your comment {# var won't be escaped #}. But I don't want you to delete this answer, It's good for others to learn about avoiding this security pitfall.
    – No Sssweat
    Jan 25, 2018 at 5:54
  • 1
    @NoSssweat This won't work for me because it only escapes the outer element. As an example with my code it will only escape <li> tag but not the string inside. I need to convert the string inside. This output is coming from item_list by auto generating the lists from Drupal. Because of that I can't grab the inside string in the <li> tag. Jan 25, 2018 at 6:15
  • 2
    @GiorgosK you cannot trust user input, you need to escape what user's put into fields/forms. Using |raw turns off Twig's auto escaping mechanism, while on the answer that I liked to the auto escaping mechanism is still on. Watch this to see why escaping is crucial.
    – No Sssweat
    Jan 25, 2018 at 13:20
  • 2
    @NoSssweat yes I understand the reasoning behind escaping but I did not know the inner working of you solution and that it was actually being escaped (thought it was raw) ... thanks for the link very informative
    – GiorgosK
    Jan 27, 2018 at 15:01
3

Try using convert_encoding('UTF-8', 'HTML-ENTITIES') twig filter. For example {{ item|convert_encoding('UTF-8', 'HTML-ENTITIES')}}.

3
  • This don't work for me :( Jan 25, 2018 at 6:11
  • This no longer works in Drupal 9, which throws the warning: Notice: iconv(): Wrong charset, conversion from HTML-ENTITIES' to UTF-8' is not allowed in twig_convert_encoding() (line 1000 of vendor/twig/twig/src/Extension/CoreExtension.php).
    – Mike Godin
    Nov 1, 2021 at 13:59
  • 2
    Just found out this doesn't work in Drupal 9 as well. What is the alternative? Jan 8 at 18:31
3

To send html markup to a template use a #markup render element

$variables['link'] = [
  '#markup' => '<a href="/this-is-new.html">This is new page</a>',
];

so that the html tags are not escaped when you output the variable in twig:

{{ link }}

The link here is only an example. There are better ways to build links, see How do I create a link?

2

But it returns the output as <li>&lt;a href="/this-is-new.html"&gt;This is new page&lt;/a&gt;</li>.

The reason for this, is because Twig is auto escaping your HTML. List only allows list related HTML tags, like <li>.

I have some list items in drupal 8

My idea was to get the output as <a href="/this-is-new.html">This is new page</a>

Don't use List as your field type, instead use Link and set it to unlimited.

enter image description here


Now when you add a new node:

enter image description here

1

You can make use Markup class which defines an object that passes safe strings through the render system.

1.) You first need to create a TwigExtension.php class inside a module and define a function which will actually return a formatted string.

2.) Use that function inside any of your twig files to render formatted strings.

3.) Do Rebuild the cache, using Drush cr.

-> Create a module_name.services.yml file under modules/module_name:

services:
  module_name.twig.TwigExtension:
    class: Drupal\module_name\TwigExtension
    tags:
      - {name: twig.extension}

-> Create a TwigExtension.php under modules/module_name/src/TwigExtension.php :

<?php

namespace Drupal\module_name;

use Drupal\Core\Render\Markup;

/**
 * Class DefaultService.
 *
 * @package Drupal\module_name
 */
class TwigExtension extends \Twig_Extension {

  /**
   * {@inheritdoc}
   * This function must return the name of the extension. It must be unique.
   */
  public function getName() {
    return 'block_display';
  }

  /**
   * In this function we can declare the extension function
   */
  public function getFunctions() {
    return array(
      new \Twig_SimpleFunction('render_string', array($this, 'render_string'), array('is_safe' => array('html'))),
    );
  }

  /**
   * Function to decode html special chars
   */
  public function render_string($string) {
    $render_string = Markup::create($string);
    return $render_string;
  }

-> **Insert twig function inside your twig file: **

{% set str = render_string('<a href="/this-is-new.html">This is new page</a>') %}
....
....
{{ str }}

Hope, this will help you.

0

Here is a special case for title field:

{# Template field--node--title--my-node-bundle.html.twig #}

{%
  set classes = [
  'field',
  'field--name-' ~ field_name|clean_class,
  'field--type-' ~ field_type|clean_class,
  'field--label-' ~ label_display,
]
%}
<span{{ attributes.addClass(classes) }}>
  {%- for item in items -%}
    {# transform title from "inline_template" render element into "markup", to avoid escaping #}
    {% set content = item.content|merge({'#type': 'markup', '#markup': item.content['#context']['value']}) %}
    {{ content }}
  {%- endfor -%}
</span>

0

You can render links with item_list. Here's an example how to from the book module:

  public function bookRender() {
    $book_list = [];
    foreach ($this->bookManager->getAllBooks() as $book) {
      $book_list[] = Link::fromTextAndUrl($book['title'], $book['url']);
    }
    return [
      '#theme' => 'item_list',
      '#items' => $book_list,
      '#cache' => [
        'tags' => $this->entityTypeManager()->getDefinition('node')->getListCacheTags(),
      ],
    ];
  }

The key is to not pass it as markup, but as a renderable array/object.

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.