Skip to content
New issue

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

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

Already on GitHub? # to your account

#each skips iteration on undefined values #1093

Closed
florianpilz opened this issue Sep 8, 2015 · 7 comments
Closed

#each skips iteration on undefined values #1093

florianpilz opened this issue Sep 8, 2015 · 7 comments

Comments

@florianpilz
Copy link

If I have an template like this:

{{#each data}}
<span class="{{@key}}">{{this}}</span>
{{/each}}

Handlebars will render nothing when data is a dict entry with empty value, i.e. if I render above template with

template({data: {
    'first': null,
    'second': 'bar',
}});

Only second will be rendered, but first will be ignored.

I have made a JSFiddle to demonstrate the behaviour:

I have tested the behaviour with 4.0.0 and it is skipped there as well, so it's a problem introduced in 4.0.0.

Is this the intended behaviour now? It is not stated in the documentation and I can't see a good match in the release notes. It breaks our application, since we expect to have 7 <td> columns rendered by Handlebars, but 2 are missing due to null values.

@kpdecker
Copy link
Collaborator

kpdecker commented Sep 9, 2015

This was changed due to #1065 where helpers fail when executing against a null context for non-strict mode. I would suggest using '' rather than null as this will render safely for all contexts. Baring that we might be able to implement a flag to allow for this unsafe iteration but I'm not sure on what sort of ETA that might have.

@Marsup
Copy link

Marsup commented Sep 13, 2015

Same problem with undefined values on lout for me where I try to iterate over objects.
I'm not sure I see why it's related to sparse arrays, can't we make a difference between objects and arrays ?

@kpdecker
Copy link
Collaborator

@Marsup the root problem is that helpers were being called like helper.call(undefined) and under non-strict mode this would execute against the global object leading to surprises. I'm now starting to think that it's better to:

  1. Execute for all defined keys (sparse arrays will skip elements)
  2. Fail over context execution to an empty object if not defined. i.e. something like helper.call(context || {})

It's not ideal but seems to provide the best middle ground for handling undefined in non-strict mode.

@Marsup
Copy link

Marsup commented Sep 21, 2015

You should probably explicitly check for null and undefined, other falsy values should work.

@kpdecker
Copy link
Collaborator

Good call, took that into account.

@kpdecker kpdecker changed the title #each skips entry of dict with empty value since 4.0.0 #each skips iteration on undefined values Sep 24, 2015
@kpdecker
Copy link
Collaborator

Released in 4.0.3

@florianpilz
Copy link
Author

Thanks, works like a charm!

nknapp added a commit that referenced this issue Mar 9, 2017
Fixes #1319

Original behaviour:
- When a block-helper was called on a null-context, an empty object was used
  as context instead. (#1093)
- The runtime verifies that whether the current context equals the
  last context and adds the current context to the stack, if it is not.
  This is done, so that inside a block-helper, the ".." path can be used
  to go back to the parent element.
- If the helper is called on a "null" element, the context was added, even
  though it shouldn't be, because the "null != {}"

Fix:
- The commit replaces "null" by the identifiable "container.nullContext"
  instead of "{}". "nullContext" is a sealed empty object.
- An additional check in the runtime verifies that the context is
  only added to the stack, if it is not the nullContext.
nknapp added a commit that referenced this issue Mar 9, 2017
Fixes #1319

Original behaviour:
- When a block-helper was called on a null-context, an empty object was used
  as context instead. (#1093)
- The runtime verifies that whether the current context equals the
  last context and adds the current context to the stack, if it is not.
  This is done, so that inside a block-helper, the ".." path can be used
  to go back to the parent element.
- If the helper is called on a "null" element, the context was added, even
  though it shouldn't be, because the "null != {}"

Fix:
- The commit replaces "null" by the identifiable "container.nullContext"
  instead of "{}". "nullContext" is a sealed empty object.
- An additional check in the runtime verifies that the context is
  only added to the stack, if it is not the nullContext.
nknapp added a commit that referenced this issue Mar 9, 2017
Fixes #1319

Original behaviour:
- When a block-helper was called on a null-context, an empty object was used
  as context instead. (#1093)
- The runtime verifies that whether the current context equals the
  last context and adds the current context to the stack, if it is not.
  This is done, so that inside a block-helper, the ".." path can be used
  to go back to the parent element.
- If the helper is called on a "null" element, the context was added, even
  though it shouldn't be, because the "null != {}"

Fix:
- The commit replaces "null" by the identifiable "container.nullContext"
  instead of "{}". "nullContext" is a sealed empty object.
- An additional check in the runtime verifies that the context is
  only added to the stack, if it is not the nullContext.
nknapp added a commit that referenced this issue Mar 25, 2017
Fixes #1319

Original behaviour:
- When a block-helper was called on a null-context, an empty object was used
  as context instead. (#1093)
- The runtime verifies that whether the current context equals the
  last context and adds the current context to the stack, if it is not.
  This is done, so that inside a block-helper, the ".." path can be used
  to go back to the parent element.
- If the helper is called on a "null" element, the context was added, even
  though it shouldn't be, because the "null != {}"

Fix:
- The commit replaces "null" by the identifiable "container.nullContext"
  instead of "{}". "nullContext" is a sealed empty object.
- An additional check in the runtime verifies that the context is
  only added to the stack, if it is not the nullContext.

Backwards compatibility within 4.0.x-versions:
- This commit changes the compiler and compiled templates would not work
  with runtime-versions 4.0.0 - 4.0.6, because of the "nullContext"
  property. That's way, the compiled code reads
  "(container.nullContext || {})" so that the behavior will degrade
  gracefully with older runtime versions: Everything else will work
  fine, but GH-1319 will still be broken, if you use a newer compiler
  with a pre 4.0.7 runtime.
nknapp added a commit that referenced this issue Mar 25, 2017
Fixes #1319

Original behaviour:
- When a block-helper was called on a null-context, an empty object was used
  as context instead. (#1093)
- The runtime verifies that whether the current context equals the
  last context and adds the current context to the stack, if it is not.
  This is done, so that inside a block-helper, the ".." path can be used
  to go back to the parent element.
- If the helper is called on a "null" element, the context was added, even
  though it shouldn't be, because the "null != {}"

Fix:
- The commit replaces "null" by the identifiable "container.nullContext"
  instead of "{}". "nullContext" is a sealed empty object.
- An additional check in the runtime verifies that the context is
  only added to the stack, if it is not the nullContext.

Backwards compatibility within 4.0.x-versions:
- This commit changes the compiler and compiled templates would not work
  with runtime-versions 4.0.0 - 4.0.6, because of the "nullContext"
  property. That's way, the compiled code reads
  "(container.nullContext || {})" so that the behavior will degrade
  gracefully with older runtime versions: Everything else will work
  fine, but GH-1319 will still be broken, if you use a newer compiler
  with a pre 4.0.7 runtime.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants