Handlebars-Net / Handlebars.Net

A real .NET Handlebars engine
MIT License
1.25k stars 214 forks source link

Async Helper Method #166

Closed starmandeluxe closed 7 years ago

starmandeluxe commented 7 years ago

Currently it seems impossible to make a registered helper method have asynchronous calls inside it. I want to do something like this:

Handlebars.RegisterHelper("my_method", async (writer, context, parameters) => { var blah = await _myService.GetBlahAsync(parameters[0]); writer.WriteSafeString(blah); });

But it ends up closing the writer even before the writesafestring is called.

Can support for this functionality be added?

rexm commented 7 years ago

Any I/O bound work you need to do should be done as part of creating the model.

starmandeluxe commented 7 years ago

Ah, I see what you mean. Normally I'd agree, but our requirement is to allow parameter passing via the template and I/O is totally based on this parameter. For example, the template will specify an ID, and the corresponding entity is then looked up via this ID during template rendering in the Helper method. I have updated my code example to reflect this.

Perhaps this is not using Handlebars as intended?

rexm commented 7 years ago

The template specifies the lookup ID? That seems odd. A template is a static asset - if you know the ID at authoring time, you can go fetch that data at initialization time and cache it for the lifetime of the template, not re-fetch each time during execution.

rexm commented 7 years ago

Or if the object could change, it should be part of a record that is tied to your template so you can look it up as part of constructing the model.

sebastienros commented 7 years ago

I have the same requirement actually.

priedthirdeye commented 7 years ago

The NodeJs hbs implementation for Express has a registerHelperAsync method. I've used it for several of my use cases.

The following post describes how it works under the hood.

https://github.com/wycats/handlebars.js/issues/141#issuecomment-44947766

If I had more experience with c# I'd try to port it over.

rexm commented 7 years ago

If this notion is incorporated into the core handlebarsjs, we'll add it as well.

Lukiya commented 6 years ago

Any update on this, I need the RegisterHelperAsync too. We'll let user edit the template, so they can call {{GetProductList 43}} to get a furniture(category id:43) picture list.

rexm commented 6 years ago

There are no plans currently to support this. The recommended architectural approach is to do all I/O bound work during the construction of your model object, not during template binding.

sebastienros commented 6 years ago

Sometimes the logic has no idea what the template might need and there is no way around it. Happens a lot in CMSs where everything is dynamic, and template can't be specialized.

As much as I understand the restriction as it's out of scope, I had to implement my own library also to overcome this limitation.

knightmeister commented 1 year ago

+1 I need this as well. Are you open to a PR to add it?

369 in some ways could obviate this to pull out IDs you need and populate the model ahead of time but given there is no ability to parse a document (as far as I can see).

I think this is a pretty crucial feature to be able to resolve dependant external inputs while generating. My specific use case is to pull information from a database and interpolate it into the output, which I would think is quite a common requirement.

rexm commented 1 year ago

Intermingling fetching and i/o with template rendering doesn’t align with the architecture of Handlebars. Candidly I think if you are headed down the road where you are using Handlebars for rendering and think you need this, you are already confused about the responsibility boundaries in your system.

knightmeister commented 1 year ago

you are already confused about the responsibility boundaries in your system.

That's a pretty blunt statement and I try to avoid dealing in absolutes; while I would generally tend to agree with you that there should be a model that populates the view, this only works ahead of time when you are doing something where all the data is known at the time the model is composed. To allow end users complete customisability you need to be able to resolve information at the time the template is rendered which is ostensibly why the helper method exists. To go beyond trivial examples (i.e. anything populated from a database or file system) you need to be able to make use of the async pattern. My particular use case is in a CMS scenario where users can embed images into the template which is resolved along with metadata and rendered into the view.

I doubt my requirements are unique and from looking at this issue, this functionality clearly is something that multiple people could make use of.

FWIW, there are other templating options out there (i.e. Liquid/Fluid) that provide the ability for helpers to be async.

Cheers

HuwLittle commented 1 month ago

+1 for this - would suit my use case perfectly - is the issue closed as you're not going to make this change?