The following works with a JS backed component (even without a showSomething property defined in JS) but not with a template only glimmer component:
{{#if showSomething}}
[Here's your something]
<button onclick={{action (mut showSomething) false}}>Hide</button>
{{else}}
<button onclick={{action (mut showSomething) true}}>Show</button>
{{/if}}
If I try the same in a template only glimmer component, I get an Assertion Failed: You can only pass a path to mut exception:

Here's a 3.5 app with a reproduction: https://github.com/GavinJoyce/template-only-mut-bug
great usecase for let helper :P
Hmm, I don't fully understand what the goal here is. Using showSomething here (unless it is passed as an argument) is ultimately going to try to do a this.showSomething lookup, but with the template only glimmer component feature flag enabled there is no this at all which is most likely causing the error you are seeing. What do you expect to be backing the mutable showSomething binding?
As a general rule any template only glimmer component's path expressions should either be a block param in scope or a named argument, anything else (IMHO) should error at compile time.
@gavinjoyce - In this scenario what would you have expected to happen?
As a general rule any template only glimmer component's path expressions should either be a block param in scope or a named argument, anything else (IMHO) should error at compile time.
I'm not 100% sure how easy or hard this would be to implement at the moment, since its unknown at template compilation if a given template is going to be used as a template only component (and frankly its possible that templates are shared so it may not be possible...).
The no-implicit-this template linting rule would be somewhat helpful here, but only in making it clearer that this is being used when it shouldn't be (and also should make the error message clearer). Docs for that here
great usecase for let helper :P
This seems to fail in the same way:
<h3>Template Only Component</h3>
{{#let false as |showSomething|}}
{{#if showSomething}}
[Here's your something]
<button onclick={{action (mut showSomething) false}}>Hide</button>
{{else}}
<button onclick={{action (mut showSomething) true}}>Show</button>
{{/if}}
{{/let}}
Hmm, I don't fully understand what the goal here is.
I'm hoping to be able to be able to implement a toggle on a local property in a template only component without needing to define a JS class.
I'm hoping to be able to be able to implement a toggle on a local property in a template only component without needing to define a JS class.
Gotcha!
on a local property
What specifically does this mean when there is no backing data structure?
FWIW, the super bad error message is definitely a bug either way. There is even a TODO in the code explaining what is happening:
One possible solution is what I submitted in a PR to your demo repo https://github.com/GavinJoyce/template-only-mut-bug/pull/1:
{{#let (data-bag) as |data|}}
{{#if data.showSomething}}
[Here's your something]
<button onclick={{action (mut data.showSomething) false}}>Hide</button>
{{else}}
<button onclick={{action (mut data.showSomething) true}}>Show</button>
{{/if}}
{{/let}}
Where data-bag returns a stable object for stashing state.
However, we should really think hard about why we want to keep this a template only component. It _does_ have its own local UI state that it is tracking...
on a local property
What specifically does this mean when there is no backing data structure?
My understanding of templates, which now appears to be wrong, was that they have a local context and that the mut helper could be used to mutate properties on that context. I can see why this mightn't be the case with template only components though.
I'm keen to replace our most frequently rendered components with template only components, mainly for performance reasons. Not being able to mutate local properties with mut and friends would likely be a barrier to removing all JS from some of these components.
One possible solution is what I submitted in a PR to your demo...
That's an interesting approach, thanks!. It seems like it gives us a way forward in our app should we feel that the benefits outweigh the possible downsides
@GavinJoyce - Depending on Ember version, I'd suggest something like sparkles-component as a possible path forward. It effectively removes any instantiation cost that Ember.Component has (it really is a _super_ tiny base class), and was created to align with future @glimmer/component APIs.
@rwjblue I'll try that, thanks.
Let's close this issue?
This thread is going to be very useful to a lot of people as template-only components become more prevalent. I had the same misunderstanding and this is the clearest explanation I鈥檝e seen.
Most helpful comment
This thread is going to be very useful to a lot of people as template-only components become more prevalent. I had the same misunderstanding and this is the clearest explanation I鈥檝e seen.