emberjs / rfcs

RFCs for changes to Ember
https://rfcs.emberjs.com/
790 stars 406 forks source link

Template string interpolation #623

Open chancancode opened 4 years ago

chancancode commented 4 years ago

Just some quick notes for the idea to open up the discussion.

It's not very common, but occasionally you need to do string interpolations in Handlebars. Today, the solution is to use the concat helper.

{{notification message=(concat @user.firstName " " @user.lastName " has added you as a friend!")}}

It's not so bad, but it does add some noise. It would be nicer, in my opinion, to have a string interpolation syntax.

Ideally, it would be nice if we could just use the normal string syntax:

{{notification message="{{@user.firstName}} {{@user.lastName}} has added you as a friend!"}}

Note that this already works in angle bracket invocations, so it aligns nicely:

<Notification @message="{{@user.firstName}} {{@user.lastName}} has added you as a friend!" />

This would work if we are designing the language from scratch. You can always escape the mustaches if you meant for them to be literal mustaches, same as any other positions. Unfortunately, this will be a breaking change. While rare, it is possible that there are existing string literals that uses mustaches for this purpose.

The good thing about this is they are by definition static. So one option is to detect existing usages of unescaped mustaches in string literals, deprecate them to clear the space, then add the interpolation feature in the next major version. In the meantime, we can create an addon for apps to opt-in.

Another option is to do what JavaScript (which had the same problem and didn't have the luxury of bumping major versions) and use a different syntax for interpolated strings, say backticks:

{{notification message=`{{@user.firstName}} {{@user.lastName}} has added you as a friend!`}}

Personally, I would prefer to do the former, as it feels more consistent overall.

In terms of implementation, ideally this would be supported in the handlebars parser directly, but if not, we can re-parse string literal AST node in our pwn parser. Because it already "works" today (kind of by definition, because you can put anything in a string literal), I don't think it will cause any technical parsing challenge/ambiguities. But this is all just a quick sketch so I could be missing something.

ro0gr commented 4 years ago

is it also supposed to work within inline helpers({{if), like:

<Notification class="{{scope}} {{if this.isSomething "{{scope}}--isSomething"}}">

it sounds like just a syntax enhancement, so it should be covered. Just making sure, I get it correct.

chancancode commented 4 years ago

@ro0gr Yeah, basically anything that works in "angle bracket string interpolation" today would work, I think. "{{"You {{"can {{"even {{"nest"}} them "}} arbitrarily"}} deep"}}". Like maybe don't do that, but it shouldn't not work.

chancancode commented 4 years ago

Hacked together a very quick proof-of-concept. A lot of cases doesn't work yet (e.g. if you nest the same kind of quotes it blows up), so please try it out and open issues for things that are failing!

gossi commented 4 years ago

I was having this case:

const template = hbs`...`;
const secondTemplate = hbs`the first ${template} wrapped`;

would that be possible with that?

chancancode commented 4 years ago

@gossi Its not related no. This is talking about string literals inside the template. You case should be solvable with components?

lifeart commented 4 years ago

@gossi https://github.com/lifeart/ember-cli-inline-template-only-components and you can do it in jsx templates in ember https://github.com/lifeart/ember-cli-jsx-templates (https://github.com/lifeart/ember-meta-explorer/blob/master/test/utils/jsx-extractor.test.js#L672)

NullVoxPopuli commented 2 years ago

The need for this comes up pretty often -- anyone want to write an RFC?

Since first-class-component-templates is proposing <template> over hbs, I don't think we need to worry about confusing backtick semantics.

sukima commented 2 years ago

This could be supported perhaps via a translation at build time which converts the strings into concat expressions.

{{notification message="{{@user.firstName}} {{@user.lastName}} has added you as a friend!"}}

Could be translated prior to being fed to the parser:

{{notification message=(concat @user.firstName " " @user.lastName " has added you as a friend!")}}
wagenet commented 2 years ago

This is a good idea, but it doesn’t seem like anyone actually wants to write the RFC. Is there anything we can do to help facilitate that?

knownasilya commented 2 years ago

This comes up more in component design with things like a button component that has a renderless option, and you can't easily use yield as you'd use the html syntax

chancancode commented 8 months ago

FYI, I archived the repo for our experimental addon. We still would very much like to see the feature, but we don't have time to push it forward, and it will require some more rigorous syntax/grammar work, and on the implementation side may be non-trivial too, if we ended up needing to stop using the stock handlebars parse (which is ultimately a good/necessary thing and overdue anyway). The good news is, with modern Ember – native classes, .gjs/.gts, etc, it usually doesn't feel too terrible to move the string concatenation into the JS side, whether as a getter, or a in-scope plain function, etc.

Still, for lots of reasons it would be nice to have a good first class syntax to do this in the template side directly.