Skip to content

Variable Directive does not work in XHTML "Text" Nodes  #6532

@astorm

Description

@astorm

Using Magento 2.1.1, as installed via composer integrator method.

The {{templateVars}} in the UI Component's xhtml rendering engine don't work as expected due to a logic error in

vendor/magento/framework/View/TemplateEngine/Xhtml/Compiler/Text.php

Steps to reproduce

  1. Install and enable to module located in https://github.com/astorm/magento2-simple-ui-component
  2. Make note of UI Component message data configuration: https://github.com/astorm/magento2-simple-ui-component/blob/master/app/code/Pulsestorm/SimpleUiComponent/view/base/ui_component/etc/definition.xml#L5
  3. Make note of XHTML template variable {{message}} https://github.com/astorm/magento2-simple-ui-component/blob/master/app/code/Pulsestorm/SimpleUiComponent/view/adminhtml/ui_component/templates/our-new-template.xhtml#L15
  4. Navigate to System -> Other Settings -> Hello Simple UI Component
  5. View rendered XHTML template

Expected result

  1. The {{message}} template variable should be replaced with the string Hello World

Actual result

  1. The {{message}} variable remains unchanged.

The problem is in the text node compiler.

#File: vendor/magento/framework/View/TemplateEngine/Xhtml/Compiler/Text.php
public function compile(\DOMText $node, DataObject $processedObject)
{
    $result = '';
    foreach ($this->directivePool as $directive) {
        $result = preg_replace_callback(
            $directive->getPattern(),
            function ($match) use ($directive, $processedObject) {
                return $directive->execute($match, $processedObject);
            },
            $node->textContent
        );
    }

    $newNode = $node->ownerDocument->createTextNode($result);
    $node->parentNode->replaceChild($newNode, $node);
}

When looping through the directive pool, the original value of the $node->textContent is always used. This means, in effect, the final directive wins and the results of the earlier directives are ignored. This means if the final directive doesn't match, the original {{message}} string ends up in the text.

Compare this to the attribute compiler

#File: vendor/magento/framework/View/TemplateEngine/Xhtml/Compiler/Attribute.php
public function compile(\DOMAttr $node, DataObject $processedObject)
{
    foreach ($this->directivePool as $directive) {
        $node->value = preg_replace_callback(
            $directive->getPattern(),
            function ($match) use ($directive, $processedObject) {
                return $directive->execute($match, $processedObject);
            },
            $node->value
        );
    }
}

Here the value of the node is changed each time through the loop, which is why variables in attributes are properly swapped. You can see this in core code with the {{spinner}} variable.

#File: vendor/magento//module-ui/view/base/ui_component/templates/listing/default.xhtml
<div
    class="admin__data-grid-outer-wrap"
    data-bind="scope: '{{getName()}}.{{getName()}}'"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../../../../Ui/etc/ui_template.xsd">
    <div data-role="spinner" data-component="{{getName()}}.{{getName()}}.{{spinner}}" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span/><span/><span/><span/><span/><span/><span/><span/>
        </div>
        <h1>This is a test</h1>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions