XaminProject / handlebars.php

Handlebars processor for php
331 stars 134 forks source link

How pass @index as parameter to custom helper #27

Closed diosney closed 10 years ago

diosney commented 10 years ago

Setup:

- A custom helper content that expects two parameters a collection and an index.

- An #each helper invocation that calls to #content helper, like this:

{{#each data}} {{#content ../parent_options @index}}{{/content}} {{/each}}

The issue:

Inside the content helper definition I want to access the @index value, say 0,1,2,... but I'm only getting a string!

How can pass as a parameter and access inside the content helper definition the value of @index?

diosney commented 10 years ago

StackOverflow question here: http://stackoverflow.com/questions/19750003/handlebars-how-pass-index-as-parameter-to-custom-helper, just in case if you want to answer there too :D

diosney commented 10 years ago

On this, what I'm trying to accomplish ultimately is to write a helper that gets as parameter an object and a index, and returns object[index](somesort,, is a little more complex than that).

Can you help me to write such a helper with Handlebars.PHP?

Thanks in advance.

everplays commented 10 years ago

handlebars.php doesn't parse arguments of helpers, it's your job to parse and give meaning to them (I'm talking about third parameter that gets passed to helpers, _helper_each($template, $context, $args, $source)). but in your case, there's no need to parse arguments, you can simply access it by $context->lastIndex().

boukeversteegh commented 10 years ago

You need to parse the arguments manually inside a helper.

$hdlbars = new Handlebars_Engine();

$hdlbars->getHelpers()->add('content', function($template, $context, $args, $source) {
    $args = explode(' ', $args);

    foreach( $args as &$arg ) {
        if( $arg == '@index' ) {
            $arg = $context->lastIndex();
        } else {
            // Parse variable name.
            $arg = $context->get($arg);
        }
    }

    $buffer = "arguments passed: " . implode(', ' $args);
    $buffer .= $template->render($context);
    return $buffer;
});
diosney commented 10 years ago

@everplays, @boukeversteegh:

1001 Thanks! With your help I was able to do the custom helper!

This was the final implementation, just for the record.

 /**
         * Handlebars Helper Content. Return object->Alphabet[index].
         *
         * @param $template
         * @param $context
         * @param $args
         * @param $source
         *
         * @return Handlebars_String
         * @throws InvalidArgumentException
         */
        public static function _helper_content($template, $context, $args, $source) {
            $Alphabet = range('A', 'Z');

            $tmp = explode(' ', $args);
            $buffer = '';

            $object = $context->get($tmp[0]);

            if (count($tmp) < 2 || $tmp[1] == '@index') {
                $last_index = $context->lastIndex();
                $letter = $Alphabet[$last_index];
            }
            else {
                $index = $context->get($tmp[1]);
                $letter = $Alphabet[$index];
            }

            $buffer .= $object->$letter;

            return new Handlebars_String($buffer);
        }
cgray commented 10 years ago

Possibly a more holistic solution would be to update the Context::get to handle the the case of @index and @key specifically as I did here

jslegers commented 10 years ago

For those interested, here's a way to create powerful templates with custom helpers, while still using but a minimal amount of code :

Template table.hbs :

{{#if table}}
<table>
    <thead>
        <tr>
            <th style="width: 2%;">#</th>
            {{#each table.0}}
            <th nowrap="nowrap">
                {{uppercase @key}}
            </th>
            {{/each}}
        </tr>
    </thead>
    <tbody>
        {{#each table}}
        <tr> 
            <th>{{increment @index}}</th>
            {{#each this}}
            <td><input type="text" value="{{this}}" /></td>
            {{/each}}
        </tr>
        {{/each}}
    </tbody>
</table>
{{#if paginator}}
<nav class="pagination">
    <ul>
        {{#each paginator}}
        <li><a href="{{this}}">{{@key}}</a></li>
        {{/each}}
    </ul>
</nav>
{{/if}}
{{else}}
<p>No data found!</p>
{{/if}}

The view :

// Create handlebars object
$template_engine = new \Handlebars\Handlebars;

// Get helpers object
$helpers = $template_engine->getHelpers();

// Add 'uppercase' helper
$helpers->add('uppercase', function($template, $context, $args, $source) {
    return ucwords($context->get($args));
});

// Add 'increment' helper
$helpers->add('increment', function($template, $context, $args, $source) {
    return $context->get($args) + 1;
});

// Render template
echo $template_engine->render(file_get_contents(__DIR__ . '/table.hbs'), array(
    'table' => array(
        array('name' => 'John', 'profession' => 'programmer', 'city' => 'Leuven'),
        array('name' => 'Sam', 'profession' => 'designer', 'city' => 'Aarschot'),
        array('name' => 'Tom', 'profession' => 'owner', 'city' => 'Leuven'),
        array('name' => 'Steve', 'profession' => 'copywriter', 'city' => 'Brussels'),
        array('name' => 'Thomas', 'profession' => 'designer', 'city' => 'Antwerp')
    ),
    'paginator' => array(
        'first' => 'http://www.test.com/first',
        '1' => 'http://www.test.com/1',
        '2' => 'http://www.test.com/2',
        '3' => 'http://www.test.com/3',
        'last' => 'http://www.test.com/last'
    )
));

The output :

<table>
    <thead>
        <tr>
            <th style="width: 2%;">#</th>
            <th nowrap="nowrap">
                Name
            </th>
            <th nowrap="nowrap">
                Profession
            </th>
            <th nowrap="nowrap">
                City
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th>1</th>
            <td><input type="text" value="John" /></td>
            <td><input type="text" value="programmer" /></td>
            <td><input type="text" value="Leuven" /></td>
        </tr>
        <tr>
            <th>2</th>
            <td><input type="text" value="Sam" /></td>
            <td><input type="text" value="designer" /></td>
            <td><input type="text" value="Aarschot" /></td>
        </tr>
        <tr>
            <th>3</th>
            <td><input type="text" value="Tom" /></td>
            <td><input type="text" value="owner" /></td>
            <td><input type="text" value="Leuven" /></td>
        </tr>
        <tr>
            <th>4</th>
            <td><input type="text" value="Steve" /></td>
            <td><input type="text" value="copywriter" /></td>
            <td><input type="text" value="Brussels" /></td>
        </tr>
        <tr>
            <th>5</th>
            <td><input type="text" value="Thomas" /></td>
            <td><input type="text" value="designer" /></td>
            <td><input type="text" value="Antwerp" /></td>
        </tr>
    </tbody>
</table>
<nav class="pagination">
    <ul>
        <li><a href="http://www.test.com/first">first</a></li>
        <li><a href="http://www.test.com/1">1</a></li>
        <li><a href="http://www.test.com/2">2</a></li>
        <li><a href="http://www.test.com/3">3</a></li>
        <li><a href="http://www.test.com/last">last</a></li>
    </ul>
</nav>

See also :