Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to pass more than one variable data to a template #1611

Closed
aimyth opened this issue Nov 23, 2019 · 12 comments
Closed

Ability to pass more than one variable data to a template #1611

aimyth opened this issue Nov 23, 2019 · 12 comments

Comments

@aimyth
Copy link

aimyth commented Nov 23, 2019

For rendering a form view the following is required at a minimum if the view is a dynamic view.
The actual schema of the fields namely a string to textbox mapping e.g FirstName which is a String rendered as TextBox, let's call this schemaData.
The data to go into the field namely for the same FirstName the value of Steve, let's call this data.
Any reference data namely for a field called State which is of type Reference a Select Box with the data coming from a State reference collection having different values for TX, NY, NJ etc let's call this statesRefData.

Though these can be rendered as partials. The data to the parent template should be a combined one as opposed to multiple data sets namely

Template(schemaData, data, statesRefData)

Within the template one should be able to reference the input param and the fields within the input param.

@nknapp
Copy link
Collaborator

nknapp commented Nov 23, 2019

You can pass an object to the template containing all the data you need.

@aimyth
Copy link
Author

aimyth commented Nov 24, 2019

Yep, that's the workaround that is being done. If these are separate data why do we need to mutate to create a combined data before passing to handlebar. From the API these come as separate data and expected to be treated as separate. Once you look at the partial template and its data the problem becomes compounded #1612.
Each partial view / template works on its own data set but the parent view has to feed the partial templates too. In a way this is an all or nothing rendering model. If we have 3 partial views within the parent view we need to wait till all the data is obtained combine these and only then we can render the view. This is exactly similar to the Server side rendering model that ASP.NET did which made to its death. The whole dynamic rendering is data comes render that part and keep rendering till you fill all the views in a page.

@nknapp
Copy link
Collaborator

nknapp commented Nov 24, 2019

Are you using Handlebars to render the complete page on the server, or do you build a single-page app, that fetches data from a rest-service and runs the template in the browser?

Server-side

I don't think there is a good way to render parts of a page before all data is assembled. Once you start sending data to the client (i.e. when the first database-call has returned), you have no way to send an error response, because the response-headers have already been sent.

Client-side

If you need partial/incremental updates of the page and you are probably much better off using a framework like Angular, Ember, React or Vue.

Handlebars is just a template-engine: It compiles a template to a function that generates an output string from an input object. It does not know about DOM and HTML. And it should not know about those things.

@aimyth
Copy link
Author

aimyth commented Nov 26, 2019

I am using at the client side.
The problem would be the same on both client-side or server-side
I agree that Handlebars should not know about DOM or HTML, but it should know about its own constructs for partials.
If it does support partials, one of the ways is the data that is supplied to the template. When you can support partials in terms of template, why not the same concept applied to the data that is supplied to the template.

So just like it combines different partials to form whole, it should have the corresponding concept for partial data to form the whole instead of expecting the data as a whole but the templating as parts.

The ask is can handlebar take more than one data pointer similar to more than one template agnostic or what is done in the render.

Once this is possible, then the usage could be enhanced to now say, you templated the whole using the parts. If a part needs to be templated again, one could ask the template engine to re-template only the part instead of the whole and get back the content. This in turn can be fed into the browser to actually render.

In abstraction
Parent Template
  Partial1Template
  Partial2Template

Similar concept in Data
Parent Data
  Partial1Data
  Partial2Data

Template Request
ParentTemplate.template(Templates = All, ParentData, Partial1Data, Partial2Data)
ParentTemplate.template(Templates = Partial1, Partial1Data)

As Opposed to keeping everything as part without parent child
Parent Template
Partial1Template
Partial2Template

Similar concept in Data
Parent Data
Partial1Data
Partial2Data

Template Request
ParentTemplate.template(ParentData)
Partial1.template(Partial1Data)
Partial2.template(Partial2Data)

Now we use other frameworks to combine in any way we want.

@nknapp
Copy link
Collaborator

nknapp commented Nov 28, 2019

The use case of partials is not to separate the whole template into three parts. You could just as well achieve this by creating multiple templates and executing them separately.

The goal of partials is the same as the goal of functions in JavaScript: extract part of the template either to reuse it in other places, or simply to increase readability.

I think you could have used something like this: https://jsfiddle.net/nmvuheLz/

But again: Once upon a time, Handlebars was a good choice to build a single-page app. It isn't anymore. The use case of Handlebars more shifted to the non-DOM rendering.

It would be interesting to know which framework you use, though.

@aimyth
Copy link
Author

aimyth commented Dec 9, 2019

In object orientation there is association which are composition and aggregation
Your solution to convert the composition relationship between templates to aggregation relationship is one way to solve but not the ideal as these are separate types.

I am not sure why you keep bringing DOM here. Rendering is generic to render the template if I have used the wrong word then we can rename this to "output of a template run with data". The problem would exist even then.

Currently, I am not using any framework. I have written the same using jquery and handlebar. I now doing exactly as you said, have a naming convention to create the composition relationship, get the data pass to the correct template, render the content and then attach to the parent.
I felt this should be the next feature to handlebar as it naturally understands the composition relationship between parent and partial templates.

I have closed the related feature. This is a relatively simple feature to support more than one input field . Are you saying this too is long term?

@nknapp
Copy link
Collaborator

nknapp commented Dec 9, 2019

I was asking about the DOM because I felt you were going to use such a combination of jquery and handlebars and I wanted to strongly advise against it.

When your application gets bigger, this is a dead end. You are always rerendering the whole page, it will be difficult to debug, because you're DOM-elements will disappear in the dev-tools even thoughbyou updating a completely part of the page. And you have to build things yourself, that you are getting for granted in other frameworks.

@nknapp
Copy link
Collaborator

nknapp commented Dec 9, 2019

I have closed the related feature. This is a relatively simple feature to support more than one input field . Are you saying this too is long term?

Maybe I still don't understand what you actually expect from the feature. What I understood is that if you have a partials:

  • partial1: p1 {{x}}
  • partial2: p2 {{x}}
  • partial3: p3 {{y}}

and a template

{{>partial1}}
{{>partial2}}
{{>partial3}}

then

template({x: 1}, {x: 2}, {y: 3})

should evaluate to

p1 1
p2 2
p3 3

If that isn't it, please correct me. But if this is what you mean, then I wouldn't put it on the roadmap at all, for the following reasons:

  1. Your example is very simple. A template and multiple partials. But you'd also have to define the behavior when the template is more complicated and I don't think that is possible.
  2. the same goal can easily be achieved by pre-processing the input, like in my earlier example
  3. It would not be backwards compatible to use the second parameters, because it is already used to pass runtime-options.

More explanation for 1 and 2:

  1. It may be easy to map multiple inputs to partials when you have a simple template like
{{> partial1 ...}}
{{> partial2 ...}}
{{> partial3 ...}}

But you can also call partials in the loop of an #each-helper (like https://handlebarsjs.com/examples/partials-register.html). You can call partials from other partials. You can even make recursive partials calls

What would you expect if you provide multiple parameters to this template: https://jsfiddle.net/jaLo28s4/

  1. There are three things that handlebars could to when confronted with multiple input objects.
  • Build a composite object like { input1, input2, input3}
  • Build an array like [ input1, input2, input3 ]
  • Merge the input objects like lodash.merge({}, input1, input2, input3)

Each of these operations is something you could easily do in a preprocessing step, but including them in the core would make Handlebars more complicated.

@aimyth
Copy link
Author

aimyth commented Dec 17, 2019

Not sure where to start. Let me take the one that makes logical sense to prove the point.

In the example provided. The expected code should be

From

var input = { arr: ['x','y','z'], deepObject: {a: {a: {a: {b:1}}}}}

document.getElementById('output').innerHTML = template(input);

To

const arrayInput = ['x','y','z'];
const deepObject = {a: {a: {a: {b:1}}}};

document.getElementById('output').innerHTML = template(arrayInput, deepObject);

Inside the template, there should be a way to access that there are multiple object passed and not just one object.
either $object1, $object2

With regard to the combination

There are three things that handlebars could to when confronted with multiple input objects.

This is again left to the caller on that single object. If in the first input they would like to do composite, next input they like array etc thats still left to the caller.

It would not be backwards compatible to use the second parameters, because it is already used to pass runtime-options.

Interesting as the other feature that I wanted to request was the runtime options should be specific to the input number. Can you point to this documentation on how to pass the run time option to the passed input namely

template(input1, input1params)

Shall create a feature request for this.

How are you saying that its not backward compatible with runtime-options in the documentation I don't see the template having more than one input.

I do agree that the simplicity would be lost but what is the plan to take it to the next level.

@nknapp
Copy link
Collaborator

nknapp commented Dec 17, 2019

How are you saying that its not backward compatible with runtime-options in the documentation I don't see the template having more than one input.

Runtime options are described in https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options

The resulting template function can be called as template(context, options) where context is the input object. options is an object that can have any of the following properties: ....

This could be presented more visibly in the documentation.

Inside the template, there should be a way to access that there are multiple object passed and not just one object.
either $object1, $object2

You can achieve exactly this by calling template like

template({object1: object1, object2: object})

and using

{{object1.property_x}}
{{object2.property_y}} 

in the template. This is event better, because you can name the objects like you want and you are not restricted to the "object"+counter notation.

You can also use the data-object

template(mainObject, { data: { object1: object1, object2: object2}})

and in the template call

{#with $object1}}...{{/with}}

{{#with $object2}}
   {{property_y}}
  {{$object1.property_x}}
{{/with}}

I haven't tested this and I am not sure if it is documented, but it should work.

Shall create a feature request for this.

We actually do have a no-new-feature policy at the moment (#1277). If anyone suggests features
I try to evaluate the value and see if I can help the issue-author with workarounds of simply point to how I think the problem should be solved (like in your case).

In some cases, features appear to be simple to implement and difficult to implement otherwise (like #1584).

I think the multiple-input scheme would make things much more complicated in the core, and I have shown multiple ways to achieve the same goal.

Unless a lot of other people also want something like that, I would like to close this issue.

@aimyth
Copy link
Author

aimyth commented Dec 22, 2019

Thanks for continuing the conversation.
In a way you are abstracting and stating that we don't need variables in a function and that all variables in a function can be merged into an object and within this object every variable can be included with variable naming
e.g if a functions has
template(var1, var2);
this could be abstracted to

object: {
  var1Name: var1,
  var2Name: var2
}
template(object);

Though this can be done. I don't agree to this one size fits all.
If you want to make sure that we meet the no-new-feature policy then its ok. I also agree that it would move away from being simple.
With regard to others not sure if its chicken and egg, if the feature was available the adoption would have been there, knowing the restriction people did find the work around similar to what I am doing now.
So go ahead and close and we can revisit when there is an appetite to add more features which are complicated to implement but easier on usage.

@nknapp
Copy link
Collaborator

nknapp commented Dec 23, 2019

Thanks for the answer. I will close this now

@nknapp nknapp closed this as completed Dec 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant