lightncandy icon indicating copy to clipboard operation
lightncandy copied to clipboard

Question: where to store data from blocks

Open flip111 opened this issue 8 years ago • 12 comments

I have a helper {{#myCustomBlock}} some data to analyze{{/myCustomBlock}} and i pass this data to a function, then i want to analyze the data and store it in a variable for later use. What is a good location to store this data? The data should be available throughout the entire template.

Can the data be safely added to the already existing data?

Perhaps it would be useful to explain somewhere in the documentation what are the options that are being passed, i noticed the array keys are this:

array(8) {
  [0]=>
  string(4) "name"
  [1]=>
  string(4) "hash"
  [2]=>
  string(8) "contexts"
  [3]=>
  string(14) "fn.blockParams"
  [4]=>
  string(5) "_this"
  [5]=>
  string(2) "fn"
  [6]=>
  string(7) "inverse"
  [7]=>
  string(4) "data"
}

some of the things are referenced in places, but not all in the same place

flip111 avatar Mar 08 '17 15:03 flip111

The $opitons object should have same behavior with handlebars.js , some document about lightncandy $options can be found here: https://zordius.github.io/HandlebarsCookbook/9002-helperoptions.html . You can see handlebars examples about options here: http://handlebarsjs.com/block_helpers.html .

To store data for inner block in handlebars you should use Handlebars.createFrame() , then pass new context with updated data into options.fn() . For lightncandy you can just pass updated data into $options.fn().

zordius avatar Mar 09 '17 01:03 zordius

I should update document for this.

zordius avatar Mar 09 '17 01:03 zordius

As i understand it passing the data to options['fn'] makes the data available in the scope of the block, what i meant with "later use" is when you already left the scope of the block, so: {{#myCustomBlock}} some data to analyze{{/myCustomBlock}} some stuff here {{#anotherBlock}}more stuff{{/anotherBlock}} INSERT DATA HERE.

flip111 avatar Mar 09 '17 08:03 flip111

Let's consider bullet point 5 from here first. createFrame seems to be used for something different, form the handlebars.js documentation:

http://handlebarsjs.com/reference.html

Used by block helpers to create a child data object.

Helpers that modify the data state should create a new frame when doing so to isolates themselves and avoid corrupting the state of any parents. Generally only one frame needs to be created per helper execution, i.e. the each iterator only creates one frame which is reused for all child execution.

flip111 avatar Mar 09 '17 09:03 flip111

Yes, I only focus on add data for inner block in previous comments. If the custom helper try to keep something for other part of the template, I do not know the correct way to do this in handlebars.js so far. Now the only way I figure out is using global and it's is dangerous for both PHP or JS.

zordius avatar Mar 09 '17 09:03 zordius

I found an example how to solve this in Handlebars.js: https://gist.github.com/yannickcr/5712791

It could be possible also with LightnCandy when the hbch method would be able to change the input data. I just changed my local Runtime to this:

public static function hbch($cx, $ch, $vars, &$op, $inverted, $cb = null, $else = null) {

Now I could update inside a helper the $options['_this'] entry. And all following helper and partials would have the data. I didn't tested if it would have any implications on other parts of the Runtime.

jenschude avatar Mar 09 '17 09:03 jenschude

According to discussions in #266 , we can try to update input context in custom helper directly. But, this is a undocumented handlebars.js behavior and it's safe to use just because JS nature. :)

zordius avatar Mar 09 '17 09:03 zordius

@jayS-de i tried this too but it doesn't work because of the dual nature of the hbch function, namely $op is used as something else in case of block custom helper or custom helper. Then you get a php error blabla can not be passed as reference. That would require a code changed as suggested in point 3 in https://github.com/zordius/lightncandy/issues/266

I can imagine this behaviour is not documented in handlebars because this is just how javascript works (not handlebards specific). If PHP would use objects to pass data it would work the same without the explicit & reference operator.

flip111 avatar Mar 09 '17 12:03 flip111

Sorry to say but i have a project to finish which can not work without this feature, so for the moment i'm switching to twig, which can do this. The reason i tried this library is because handlebars has much better cross language support. I'm prototyping some stuff in PHP that later might be integrated into C. Yes even C has a handlebars library! There is no C library for twig, so i will have to worry about this later but at least i can get the php prototype done. I could also use javascript for the prototype but i prefer to use php rather than node.js (would need node.js for reading files and such). I think porting the template back from twig to handlebars would not take that long.

flip111 avatar Mar 09 '17 12:03 flip111

Update: Now custom helpers can update $options['_this'] just like handlebars.js (updating this) and the change will keep persistently. (example: https://github.com/zordius/lightncandy/blob/8283c22e10c3a98c036a135243d076ba0e7b8566/tests/regressionTest.php#L1343-L1359 )

zordius avatar Mar 24 '17 03:03 zordius

Follow up: document

zordius avatar Mar 24 '17 03:03 zordius

@flip111 @zordius I know this is an old issue but you and others might find this useful. I developed this helper for my own needs and it should do exactly what you wanted @flip111.

Here is the helper:

'global' => (function() {
    $vars = [];

    return function($key, $value) use (&$vars) {
        $args = func_get_args();
        $options = array_pop($args);

        if (count($args) == 1) {
            return isset($vars[$key]) ? $vars[$key] : null;
        } else {
            $vars[$key] = $value;
        }
    };
})()

Here is an example:

{{#each tables}}
    {{global "first" null}}
    {{#each rows}}
        {{#unless (global "first")}}
            {{global "first" true}}
            <!-- so something with the first row of this table -->
        {{/unless}}
    {{/each}}
{{/each}}

P.S. set/get calls need to be in the same helper. Otherwise it will not work.

tunnela avatar Feb 08 '19 16:02 tunnela