ember.js: Rendering

In ember 3.3.0 following template breaks application:

{{#each buttons as |button|}}
  <button>{{button}}</button>
{{/each}}

buttons is a simple array. In console I can see Uncaught Error: unreachable. This error does not happen with other tags (i.e. works fine) and does not happen on ember 3.2.2

Repository with demonstration: https://github.com/GendelfLugansk/ember-rendering-bug

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 44 (25 by maintainers)

Commits related to this issue

Most upvoted comments

Is this considered a breaking change?

Yes, it is a breaking change and we are working on the fix.

I’m also a little confused by the RFC as to whether this is allowed (and supposed to work), or allowed at build-time (but breaks at run-time).

The current behavior matches what the RFC stated (essentially that a block param named button + <button></button> in the blocks template, would assume that the block param was a yielded closure component and attempt to render it as such), but based on feedback (here and in other issues) we determined that the result is definitely something we consider a breaking change.

The current plan (after discussing with the core team on 2018-07-20) is:

  • Ensure that all block params that “shadow” normal HTML elements inside their blocks, continue to render the HTML element (and do not assume they should “invoke” a yielded component).
  • Issue a deprecation when this scenario is detected (with until: '4.0.0')
  • Ensure the linter is setup for newly generated Ember apps that will lint against using confusing block param names (where the block param could be an HTML element name, and that HTML element is invoked in the blocks template).
  • Backport these fixes to Ember 3.3

This change should not have happened during 3.3, and we will try to figure out why it did (and fix it). However, we do intend for it to land as part of 3.4 (along with support for linting in applications as a general guide).

Ya, I mentioned above that we consider this a bug and totally want to make the ergonomics much better (e.g. issue a warning that has a helpful message instead of error).

@nightire as I can tell, we can use html tag name in as part if that html tag is not used inside block. I also think it affects not just each but any block helper that yields something, including our own components.

Changing to:

{{#each buttons as |text|}}
  <button>{{text}}</button>
{{/each}}

Should resolve the issue (assuming that my guess RE: angle bracket invocation feature is correct).

@pzuraq yes, I can reproduce this error using 3.8.0

Probably, but its still a good idea to refactor away from the patterns that are currently breaking.

The ember-template-lint rule no-shadowed-elements is a great way to enforce this.

Just another information: this issue tricks me a lot, the key is: don’t use html tag name as the as part of each any helper that yields something.

{{#each people as |p|}} {{!-- don't, because `p` is a html tag name --}}
  {{p.name}}
{{/each}}

{{#each people as |person|}} {{!-- this is fine --}}
  {{person.name}}
{{/each}}

I’m not sure if it will be a breaking change, but I hope not.

FYI just spent a couple hours on debugging til I finally googled and found this issue (was using option and the yielded name) so its still out there on 3.15.

What was weird was that {{log option}} reported the correct value inside the loop, so never occurred to me the name of the variable could be the problem

@BobrImperator I’m not sure that problem is something that is possible to catch ahead of time, and it should have always been a failure because local variables have always taken precedence over globals. The reason we can’t catch it ahead of time is similar to why you can’t lint against something like this in JS:

function foo() {

}

function bar(foo = 'a string') {
  foo(); // Error: foo is not a function
}

While it’s reasonable to assume that this is a shadowing mistake on the developer’s part, because of JS’s dynamic nature, we can’t know for sure that foo is not a function when being passed into bar. If we were using a typed language instead, then we may be able to, but unfortunately I don’t think we can currently.

@rwjblue No worries! If the linter picks it up then that is good. It is only a result of bad coding anyway.

Just throwing this out there for more documentation on the issue.

👍 thanks for that (and sorry for the issue)

Confirm, yielding any block param that will be used as an angle bracket invocation will result in this issue.

See the following issues for some more background information: