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

Is it possible to render a partial without the parent scope? #1032

Closed
aputinski opened this issue May 27, 2015 · 13 comments
Closed

Is it possible to render a partial without the parent scope? #1032

aputinski opened this issue May 27, 2015 · 13 comments
Milestone

Comments

@aputinski
Copy link

I'm trying to treat partials as "components" that only have access to variables that were passed in as attributes — is this possible?

my-partial.hbs

<div class="my-partial">
  <!-- should only be displayed if it was passed in as an attribute -->
  {{#if name}}
    Hello, {{name}}
  {{/if}}
</div>

my-app.js

const data = { name: 'Adam' }

my-app.hbs

Hello, my name is {{name}} and is available in the template scope
{{> myPartial}}
{{> myPartial name="John"}}

Actual output

Hello, my name is Adam and is available in the template scope
<div class="my-partial>
  Hello, Adam
</div>
<div class="my-partial>
  Hello, John
</div>

Desired output

Hello, my name is Adam and is available in the template scope
<div class="my-partial"></div>
<div class="my-partial">
  Hello, John
</div>
@aputinski
Copy link
Author

Here's my current workaround:

Handlebars.registerHelper('component', (partial, options) => {
  const template = Handlebars.compile(Handlebars.partials[partial])
  const html = template(options.hash)
  return new Handlebars.SafeString(html)
})
{{component "myPartial" name="Adam"}}

@kpdecker
Copy link
Collaborator

Passing a first argument to the partial call will render in its own context, independent of the parent. I.e. {{> myPartial newContext name="foo"}}

@rayHInstart
Copy link

Why is that not the default behavior? That was a rude shock when I found out.

@kpdecker
Copy link
Collaborator

kpdecker commented Aug 8, 2015

How exactly would we default to providing a context from an arbitrary variable without the author selecting that variable by name?

@rayHInstart
Copy link

I'm not sure I understand. If a set of key-value pairs were provided
that would form the object used as the context. For example if I had a
partial like this:

{{> radioButton name="enabled" value="true" id="enableUser"
labelText="Enable User"}}

The object used as a context would be:
{
name: "enabled",
value: "true",
id: "enableUser"
labelText: "Enable User"
}

I don't know how it works now internally (I'm just a user).

On 8/7/15, Kevin Decker [email protected] wrote:

How exactly would we default to providing a context from an arbitrary
variable?


Reply to this email directly or view it on GitHub:
#1032 (comment)

@kpdecker
Copy link
Collaborator

@rayHInstart Those keys are added to the existing context to form a new context that has both sets of data, with the explicitly passed hash parameters given priority. This has a performance impact vs. some of the alternatives, but outside of that I'm not sure what was quite so rude or shocking. Glad to discuss any changed to behavior you'd like to see but please outline it fully as well as outline the issues that you've seen from the existing behavior.

@rayHInstart
Copy link

In my use case, I had a partial which used a common variable name,
something like

{{#if disabled}}Hi, I'm disabled{{/if}} {{#unless disabled}}I'm super active!{{/unless}}

And I used it like this:
{{> superPartial }}

, expecting that since I did not explicitly pass a disabled into the
context, it would assume that disabled wasn't present. Unfortunately,
I had a completely unrelated variable named "disabled" in the template
object, so the partial kept rendering as if disabled were true, even
though I hadn't passed it in to the partial. I didn't expect this
behavior at all, and it took me quite a while to figure out what was
going on (couldn't easily find in the documentation a description of
this behavior). The partial is in a separate file, and I'm passing in
variables to the partial, so I assumed that the partial would always
have a completely different context than the parent. Since partials
are intended to be reused as parts of different templates, I would
think that their context would be compartmentalized away from the
parent context -- the partial shouldn't rely on the parent template
since that breaks encapsulation. I imagined the partial to be a
function which I was passing arguments to. I'm very glad that
Handlebars allows this capability if I tell it explicitly that I want
a new context for the partial, but to me it's counterintuitive that I
would have to tell it to do that. I think the expected behavior should
be that a partial always has its own context; if a user wants to
override and inherit from the parent context, there can be a flag to
do that, but it should be explicit.

On 8/12/15, Kevin Decker [email protected] wrote:

@rayHInstart Those keys are added to the existing context to form a new
context that has both sets of data, with the explicitly passed hash
parameters given priority. This has a performance impact vs. some of the
alternatives, but outside of that I'm not sure what was quite so rude or
shocking. Glad to discuss any changed to behavior you'd like to see but
please outline it fully as well as outline the issues that you've seen from
the existing behavior.


Reply to this email directly or view it on GitHub:
#1032 (comment)

@kpdecker
Copy link
Collaborator

Regarding documentation, the behavior is covered here but as with all documentation, what makes sense to someone who already knows the code is not always the same for others.

Will render the partial named myPartial. When the partial executes, it will be run under the current execution context.

I did not write the original implementation of this so I can't really comment on why the decisions were made as they were, but I can say that changing this is difficult. Many of these arguments are completely valid but like most issues filed with Handlebars of late, counter arguments can be made to equal degree, and for those cases "don't break existing code" generally wins.

I don't think it will be much overhead to add a flag that requires explicit contexts to be passed for partials which I can look at for this release, but we aren't going to be able to change the existing behavior. This does little to account for confusion of users just starting with the language, but would allow for your use case with out nasty temporary objects or similar hacks.

@kpdecker kpdecker reopened this Aug 14, 2015
@kpdecker kpdecker added this to the 4.0 milestone Aug 14, 2015
@rayHInstart
Copy link

Thanks Kevin. "Don't break existing code" is indeed a winning
argument. That is a flag I would definitely use :).

On 8/14/15, Kevin Decker [email protected] wrote:

Regarding documentation, the behavior is covered
here but as with all documentation,
what makes sense to someone who already knows the code is not always the
same for others.

Will render the partial named myPartial. When the partial executes, it
will be run under the current execution context.

I did not write the original implementation of this so I can't really
comment on why the decisions were made as they were, but I can say that
changing this is difficult. Many of these arguments are completely valid but
like most issues filed with Handlebars of late, counter arguments can be
made to equal degree, and for those cases "don't break existing code"
generally wins.

I don't think it will be much overhead to add a flag that requires explicit
contexts to be passed for partials which I can look at for this release, but
we aren't going to be able to change the existing behavior. This does little
to account for confusion of users just starting with the language, but would
allow for your use case with out nasty temporary objects or similar hacks.


Reply to this email directly or view it on GitHub:
#1032 (comment)

@kpdecker
Copy link
Collaborator

kpdecker commented Sep 1, 2015

Released in 4.0.0

@arthwood
Copy link

To reference local variable specifically inside partial and not allow to "leak" from the outer context use:

@this.myVariable

@andreyvolokitin
Copy link

this doesn't work as a local context reference (@this doesn't work either).

Is it possible to get a reference to the local context? If we have clashing name from some parent context, we could just reference a local context specifically inside a partial: localCtx.clashingVar. If it's not possible, how difficult is to implement this? Empty dumb context as the first partial argument just to "erase" parent context seems like littering in the code... An introduced flag is also seems not ideal, because sometimes you want to use parent by default (actually I relied on this until I encountered this issue). So, essentially it seems like it is best to allow choosing a context to use — parent by default, and local with some keyword reference. /cc @kpdecker

@imike57
Copy link

imike57 commented Mar 1, 2022

local context variable still do not exist ? I would just like to access hash arguments like it's possible in a helper with options.hash. How to get only the hash arguments in a partial ?

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

6 participants