Blaze Bugs when passing callbacks

Hi Everybody!
I’m experiencing a weird thing with Blaze when passing callbacks from helpers that receive a parameter. An example:

<template name="example" args="parameter">
    {{> childTemplate callback=(callbackGenerator parameter)}} 
</template>
Template.example.helpers({
    callbackGenerator(parameter) {
        return () => {
            console.log(parameter);
        };
    }
});

I expected the callbackGenerator to return a function, and this function to be called when childTemplate calls it.
But what actually happens is that the function returned is getting immediatly called. It doesn’t happen with callbacks without parameters though.
To fix that, we have to write this:

Template.example.helpers({
    callbackGenerator(parameter) {
        return () => {
            return () => {
                console.log(parameter);
            };
        };
    }
});

Is really ugly :disappointed:

Anything i’m doing wrong?

Wrap it in another function and you’re set. No lie

Yes that what’s we are doing, but in my opinion this is a bug!
Because if you don’t pass any parameter is working perfectly.
And using two functions to return one is super ugly.
My question is: is there anyway to do it without two functions?

I think you shouldnt rely on blaze helpers to pass down functions. Just pass data and have some function down the line process it.

I’m just following blaze guidelines from the meteor guide:
Passing down callbacks.

1 Like

Sure both those examples return an object (data) not a func

But why is it working without parameters, but not with parameters?
Because its working perfectly without any paramter passed to the helper.

I think that the parameters tell Blaze to execute the function:

Without parameter:

call = callbackGenerator;

With parameters:

call = callbackGenerator(parameter);

The thing is that is executing both functions, the helper and the returned
function.
In my opinion, this is a bug. Is calling the value returned by the helper.
That’s weird
Any of the @meteor core could say something about that? I would write a PR
to fix this if it is not done on purpose for any reason.

@sashko do you have any idea?
I just want to know if it worth writing the PR

Unfortunately Blaze kind of calls functions whenever it wants to, so I don’t know if there is a good solution to this.

For me it seems that the Spacebars parser is doing something like this:

<template name="example">
{{#let variable=(helper param)}} => {{#let variable=functionReturned}} ( in Spacebars functions are called just by naming them)
{{/let}}
</template>

What I mean is that when using parenthesis, Spacebars is doing an intermediate step, replacing the expression between parenthesis for what it returns, and then is compiling again the template.
Without parenthesis is not happening because it compiles it directly. Thats what I supose

Mhh…did you try if your example above works with a Blaze exentension like http://viewmodel.org ?

I don’t want to use any extension because i want just to keep it simple blaze.
I’ve fixed it wraping the Spacebars.call function:

const oldCall = Spacebars.call;

Spacebars.asValue = function (fn) {
    fn.asValue = true;
    return fn;
};

Spacebars.call = function (value, ...args) {
    // Don't call asValue functions
    if (typeof value === 'function' && value.asValue) {
        value.asValue = undefined;
        // Automatically wrap functions that shouldnt be called
        return oldCall.apply(null, [() => value, ...args]);
    }

    return oldCall.apply(null, [value, ...args]);
};

Then my helper will look like:

helper(param) {
    return Spacebars.asValue(() => { console.log(param) });
}
<template name="example" args="param">
    {{> childTemplate callback=(helper param)}}
</template>

That works.

At the end, we approach it with a simpler solution
We will wrap all functions with a class EventHandler

class EventHandler {
     constructor(fn) {
          this.fn = fn;
     }
     call() {
         this.fn.apply(null, arguments)
     }
}
helper(param) {
    return new EventHandler(() => 1);
}

'eventHandlerInChild'(event, instance) {
    instance.data.eventHandler.call('someData');
}