-1

I've got the element with following plain string (result of some kind of side effect):

<drupal-render-placeholder callback="Drupal\block\BlockViewBuilder::lazyBuilder" arguments="0=myform&amp;1=full&amp;2" token="p8BOEzhVedaNVMwuFKbSbXYvHJtJev-99V8a_12Hv4A"></drupal-render-placeholder>

Which method/function can I call to manually replace this render placeholder with relevant content?

My idea as a workaround is to do this:

/**
 * Implements template_preprocess_form_element
 */
function mytheme_preprocess_form_element(&$variables) {
  if ($variables['element']['#title'] == 'Specific Title') {
    $variables['element']['#value'] = REPLACE_PLACEHOLDER($variables['element']['#value']);
  }
}

Note that in $variables['children'], the placeholder looks in different format for some reason:

<input data-drupal-selector="edit-taps" type="hidden" name="myform" value="1:&lt;drupal-render-placeholder callback=&quot;Drupal\block\BlockViewBuilder::lazyBuilder&quot; arguments=&quot;0=myform&amp;amp;1=full&amp;amp;2&quot; token=&quot;p8BOEzhVedaNVMwuFKbSbXYvHJtJev-99V8a_12Hv4A&quot;&gt;&lt;/drupal-render-placeholder&gt;:2" />

1

You can't do this, at least not with the data you've shown in the question.

You need the placeholder attached to #attached to execute the replacement. When rendering normally this bubbles up to the page level. But this looks like early rendering in such a way that the metadata is lost, otherwise you wouldn't report the issue.

For a workaround, why you don't replace the value with the render element of the webform you intend to place here:

$variables['element']['#value'] = [
  '#type' => 'webform',
  '#webform' => 'myform',
  '#default_data' => [],
];

This could be the final solution, if you use a custom token pattern for replacement.

0

This is the workaround which worked for me:

use Drupal\block\BlockViewBuilder;
use Drupal\Core\Render\Markup;

/**
 * Implements template_preprocess_form_element
 */
function mytheme_preprocess_form_element(&$variables) {
  if ($variables['element']['#type'] == 'webform_computed_token') {
    $block = BlockViewBuilder::lazyBuilder('myform', 'full', 2);
    $block_content = render($block);
    $variables['children'] = Markup::create($block_content);
  }
}

I've used $variables['children'], since the final rendered content was in there (not in $variables['element']). Above code has hardcoded lazyBuilder method with the arguments based on the data which are included in <drupal-render-placeholder>, since reading plain text and parsing the attribute values only to read which callback to call with which arguments would be too complex (as I didn't find any straight functions to read these placeholder tags, however I've posted complete solution below).

The above solution is basically the dirty workaround for the mentioned side effect (currently unknown bug) as normally render placeholders should be part of the #attached element (see createPlaceholder() in PlaceholderGenerator.php).


Here is the complete workaround which it did work for me (placed in: mytheme.theme):

use Drupal\Component\Utility\Html;
use Drupal\Core\Render\Markup;
use Drupal\block\BlockViewBuilder;

/**
 * Implements template_preprocess_form_element
 */
function mytheme_preprocess_form_element(&$variables) {
  if ($variables['element']['#type'] == 'webform_computed_token') {
    // Dirty workaround for this bug: https://www.drupal.org/node/2899439;
    $dom = new DOMDocument();
    libxml_use_internal_errors(true); // Surpress the warnings for HTML load.
    $dom->loadHTML($variables['element']['#value'] ?: (string) $variables['children']);
    $placeholder = $dom->getElementsByTagName('drupal-render-placeholder');
    if (!empty($placeholder[0])) {
      $callback = $placeholder[0]->getAttribute('callback');
      $args = parse_str(Html::decodeEntities($placeholder[0]->getAttribute('arguments')), $args_arr);
      $element = call_user_func_array($callback, $args_arr);
      $content = render($element);
      $variables['children'] = Markup::create($content); // Replace the old markup.
    }
  }
}
0

The result of drupal-render-placeholder tag inside final output isn't expected and this usually indicates the problem how the content is rendered.

So instead of replacing placeholder manually as in the other answer, rendering method should be fixed by using renderer service.

The common mistake is to use drupal_render($content) instead of \Drupal::service('renderer')->renderRoot($block_content).

My issue was related to Block Token bug and example fix can be seen in this patch file.

  • Don't replace render() with renderRoot(), like you did in a lot of edits in other posts, because render() is used in most cases within a render context and you need render() to bubble up metadata. You only get trouble when you render too early without a render context. This produces not only orphaned placeholder tags like in your case, a lot of more bad things can happen. – 4k4 Aug 5 '17 at 12:57

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.