zordius / lightncandy

An extremely fast PHP implementation of handlebars ( http://handlebarsjs.com/ ) and mustache ( http://mustache.github.io/ ),
https://zordius.github.io/HandlebarsCookbook/
MIT License
610 stars 76 forks source link

Assigning Helper Return Values to Partial Parameters #294

Open jasonh-brimar opened 6 years ago

jasonh-brimar commented 6 years ago

The PHP Code:

require('./vendor/autoload.php');

// Define the template string.
// $templateString = '{{> MyPartial name="John Doe" message="Hello World!"}}'; // This template works.
$templateString = '{{> MyPartial name="John Doe" message=(echo message="Hello World!")}}'; // This template does NOT work.

// Define the partial template string.
$partialTemplateString = '{{name}} says: “{{message}}”';

// Define helpers...
$helpers = [
    'echo' => (
        function ($options)
        {
            return $options['hash']['message'];
        }
    )
];

// Define the LightnCandy compile options.
$compileOptions = [
    'flags' => LightnCandy\LightnCandy::FLAG_HANDLEBARSJS_FULL | LightnCandy\LightnCandy::FLAG_ERROR_SKIPPARTIAL | LightnCandy\LightnCandy::FLAG_EXTHELPER | LightnCandy\LightnCandy::FLAG_ERROR_EXCEPTION,
    'helperresolver' => (
        function ($context, $name) use ($helpers): bool
        {
            return array_key_exists($name, $helpers);
        }
    )
];

// Create the template using LightnCandy.
$template = eval(LightnCandy\LightnCandy::compile($templateString, $compileOptions));

// Define the LightnCandy render options.
$renderOptions = [
    'partials' => [
        'MyPartial' => eval('use \LightnCandy\Runtime as LR; use \LightnCandy\SafeString as SafeString; return ' . LightnCandy\LightnCandy::compilePartial($partialTemplateString, $compileOptions) . ';')
    ],
    'helpers' => $helpers
];

?>
<!doctype html>
<html lang="en">

    <head>
        <meta charset="utf-8">
        <title>Nested Helper Test</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <style>
            #php, #js {
                border: 1px solid black;
                margin: 20px;
                padding: 20px;
            }
            #php {
                background: #ffe;
            }
            #js {
                background: #eff;
            }
        </style>
    </head>

    <body>

        <div id="php">
            <h1>Rendered by PHP</h1>
            <div id="render-php"><?= $template(new stdClass(), $renderOptions) ?></div>
        </div>

        <div id="js">
            <h1>Rendered by JS</h1>
            <div id="render-js"></div>
        </div>

        <script id="template" type="text/x-handlebars-template"><?= $templateString ?></script>
        <script id="partial-template" type="text/x-handlebars-template"><?= $partialTemplateString ?></script>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.js" integrity="sha256-JWyJjSicZs/EX4AJmuCHSYYARSvIkYeM79Dn1pJOSCE=" crossorigin="anonymous"></script>

        <script>

            // Register helpers.
            Handlebars.registerHelper({
                echo: function (options) {
                    return options.hash.message;
                }
            });

            // Make the partial template available as a partial.
            Handlebars.registerPartial("MyPartial", Handlebars.compile(document.getElementById("partial-template").innerHTML));

            // Compile the the template and render.
            document.getElementById("render-js").innerHTML = Handlebars.compile(document.getElementById("template").innerHTML)({});

        </script>

    </body>

</html>

The Issue:

Partial parameters that are assigned a value by way of a helper are not accessible within the partial when compiled using LightnCandy. LightnCandy generates John Doe says: “” where Handlebars generates John Doe says: “Hello World!”.

Changing $templateString to the commented-out alternative from the code sample works properly by bypassing the use of the echo helper.

friedlink commented 5 years ago

I think that I am experiencing the same issue as this. I am attempting to use a helper in a subexpression to set a partial parameter.

PHP Code:

require('./vendor/autoload.php'); 

// Define the template string.
// $templateString = '{{> MyPartial name="John Doe" message="Hello World!"}}'; // This template works.
$templateString = '{{> MyPartial name="John Doe" message=(concat "Hello" " World!")}}'; // This template does NOT work.

// Define the partial template string.
$partialTemplateString = '{{name}} says: “{{message}}”';

// Define helpers...
$helpers = [
    'concat' => (
        function (...$parts) {
            // Remove options from parts.
            $options = array_pop($parts);

            // Join parts with separator.
            return join($options['hash']['separator'] ?? '', $parts);
        }
    )
];

// Define the LightnCandy compile options.
$compileOptions = [
    'flags' => LightnCandy\LightnCandy::FLAG_HANDLEBARSJS_FULL | LightnCandy\LightnCandy::FLAG_ERROR_SKIPPARTIAL | LightnCandy\LightnCandy::FLAG_EXTHELPER | LightnCandy\LightnCandy::FLAG_ERROR_EXCEPTION,
    'helperresolver' => (
        function ($context, $name) use ($helpers): bool
        {
            return array_key_exists($name, $helpers);
        }
    )
];

// Create the template using LightnCandy.
$template = eval(LightnCandy\LightnCandy::compile($templateString, $compileOptions));

// Define the LightnCandy render options.
$renderOptions = [
    'partials' => [
        'MyPartial' => eval('use \LightnCandy\Runtime as LR; use \LightnCandy\SafeString as SafeString; return ' . LightnCandy\LightnCandy::compilePartial($partialTemplateString, $compileOptions) . ';')
    ],
    'helpers' => $helpers
];

?>
<!doctype html>
<html lang="en">

    <head>
        <meta charset="utf-8">
        <title>Nested Helper Test</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <style>
            #php, #js {
                border: 1px solid black;
                margin: 20px;
                padding: 20px;
            }
            #php {
                background: #ffe;
            }
            #js {
                background: #eff;
            }
        </style>
    </head>

    <body>

        <div id="php">
            <h1>Rendered by PHP</h1>
            <div id="render-php"><?= $template(new stdClass(), $renderOptions) ?></div>
        </div>

        <div id="js">
            <h1>Rendered by JS</h1>
            <div id="render-js"></div>
        </div>

        <script id="template" type="text/x-handlebars-template"><?= $templateString ?></script>
        <script id="partial-template" type="text/x-handlebars-template"><?= $partialTemplateString ?></script>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.js" integrity="sha256-JWyJjSicZs/EX4AJmuCHSYYARSvIkYeM79Dn1pJOSCE=" crossorigin="anonymous"></script>

        <script>

            // Register helpers.
            Handlebars.registerHelper({
                concat: function (...parts) {
                    // Remove options from parts.
                    options = parts.pop();

                    // Join parts with separator.
                    return parts.join(options.hash.separator !== undefined ? options.hash.separator : '');
                }
            });

            // Make the partial template available as a partial.
            Handlebars.registerPartial("MyPartial", Handlebars.compile(document.getElementById("partial-template").innerHTML));

            // Compile the the template and render.
            document.getElementById("render-js").innerHTML = Handlebars.compile(document.getElementById("template").innerHTML)({});

        </script>

    </body>

</html>
jasonh-brimar commented 4 years ago

I can confirm that this issue is still present in LightnCandy 1.2.5.