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

Change attribute/property binding syntax to require opt-in to properties. #399

Closed
justinfagnani opened this issue Jul 12, 2018 · 60 comments · Fixed by #398
Closed

Change attribute/property binding syntax to require opt-in to properties. #399

justinfagnani opened this issue Jul 12, 2018 · 60 comments · Fixed by #398

Comments

@justinfagnani
Copy link
Collaborator

The current syntax of lit-extended sets properties by default and requires opting out of properties to set attributes with the $ suffix.

This has a number of downsides:

  • The default of setting properties is different from plain HTML
  • When you add a binding to an attribute it changes from an attribute to a properties
  • There's no syntax indication that some behavior is different from HTML
  • Case-sensitive attributes like viewBox are harder to implement
  • The syntax isn't a super-set of lit-html core

This issue is to document the rationale for changing the syntax to set attributes by default and require opt-in to properties with a . prefix.

Compison

Current syntax:

html`<input id$=${id} value=${value} required?=${required} on-change=${onChange}>`

Proposed syntax:

html`<input id=${id} .value=${value} required?=${required} on-change=${onChange}>`

And for consistency, we could go further and make all binding modifiers be single-symbol prefixes, like:

html`<input id=${id} .value=${value} ?required=${required} @change=${onChange}>`
@justinfagnani
Copy link
Collaborator Author

I'm liking the last option the more I look at it. It places the modifier in a consistent place and away from the other symbols required for a binding (=${ or ="${) so it stands out more.

I think it'll be nice with a lot of bindings, when I typically put each attribute on a separate line:

html`<input
       id=${id}
       .value=${value}
       ?required=${required}
       @change=${onChange}>`

There the binding type is very clear.

The downside is that @ is a new symbol and might not be obvious to first-time readers. On the other hand we should optimize for the repeat reading of daily users.

@idibidiart
Copy link

Definitely the last option. The one right under Proposed Syntax feels very confusing to me, personally.

@motss
Copy link

motss commented Jul 12, 2018

How about this version? This is something that we are already using but if possible on-change is preferable instead of introducing more symbols.

html`<input
             id=${id}
             value.=${value}
             required?=${required}
             change@=${onChange}>`

@idibidiart
Copy link

I personally prefer the clean-slate (everything changes together consistently) including where you put the modifier. Everything else has a bit of hysteresis.

@justinfagnani
Copy link
Collaborator Author

@motss my problem with that is the run of symbols between the attribute name and value. The modifier gets very lost, imo.

@idibidiart
Copy link

The bias for me being left to right is how I read, and what @justinfagnani just stated.

@bahrus
Copy link

bahrus commented Jul 12, 2018

My vote is:

html<input id=${id} value:=${value} required?=${required} change+=${onChange}>

But I haven't really thought it through.

@justinfagnani
Copy link
Collaborator Author

And regarding a new symbol: I partially agree, but after looking at the examples for a bit I find on- to stick out. I also like that all the prefixes don't work with setAttribute() so they're very unlikely to clash with real attributes.

@appsforartists
Copy link

A nice side-effect of @change is that it's super clear that you are using lit sugar (which has its own semantics, and perhaps a whitelist of supported values). It doesn't resemble early 90s inline event listeners or anything else that could be built-in to HTML.

One of the painful bits of React is that onMouseMove is built-in and onPointerMove wasn't for a long time. When you get no events, it's not obvious if it's because none were fired or because the library didn't register to listen to them.

@socceroos
Copy link

The third option seems the most consistent to me; and as @appsforartists just said, @<event> is growing on me as an easy way to distinguish magical lit sugar from standards.

@matthewp
Copy link

Special symbols and characters are something you usually regret over time. It's harder for people to learn. on-click is something everyone immediately understands.

Defaulting to attributes is definitely the right call though!

@CaptainCodeman
Copy link

I like the .prop, ?optional and @event character prefixes. Well worth the change long-term and better than the current way where normal looksjustlikeanattribute="" really means a property but isthisanattribute$ is an attribute. Also, I think the on- prefix for event handlers is slightly confusing and people don't always realize it's part of lit compared to the normal platform on... handlers

Feels like the prefix characters being first would also make things slightly easier when you have to create regexes for tools like html minifiers to ignore certain parts of the templates. It is also nicer having them away from the ${} part plus they would line up if put onto separate lines (all the props together etc...)

"make it so!" in best Jean-Luc Picard voice ...

@ruphin
Copy link
Contributor

ruphin commented Jul 12, 2018

Overall huge fan of the proposed attribute by default, property with dot prefix. And moving the boolean attribute to prefix for consistency seems good too.

I'm on the fence regarding the @event syntax. I'm mostly concerned about introducing too many symbols, making html harder to parse at a glance. It's starting to exceed my weirdness budget, as you folks like to say. On the other hand I'm usually initially heavily biased against change, so if I'm neutral about it now I'll probably think it's a good idea later.

@bahrus
Copy link

bahrus commented Jul 12, 2018

I'm really ambivalent, just throwing out concerns in case any of them might be important. Yeah, if easier to parse, that's a strong argument. On an aesthetic level, I remember the Scala language "guy" explaining why he put types after the variable name, which seems to be a trend. What's important comes first. What type it is comes after, because it's a technical detail.

But, yeah, it does mean more non-ascii characters together.

@CaptainCodeman
Copy link

CaptainCodeman commented Jul 12, 2018

I'm wondering if !event works better than @event? For me it denotes 'action' more which fits better and the @ is a bit 'heavy' and '!' should align with the '.' prefix and fit with '?' as well ... all "dot at the bottom"

It might also save a few unintentional @mentions when snippets are copy / pasted into slack / twitter etc...

@ruphin
Copy link
Contributor

ruphin commented Jul 12, 2018

Good points there. I really like the !event suggestion. I agree that the problem is mostly the @ symbol, it just seems out of place, and is used too much in other contexts which can cause issues.

@idibidiart
Copy link

The @ is also the decorator symbol in JS, or was the decorator feature dropped from spec? Either way, Angular uses @ as a decorator.

@jwoyame
Copy link

jwoyame commented Jul 12, 2018

I think it is great if non-symbolic names always produce attributes. It is a very intuitive and easily-taught rule. For that reason, I lean toward either @event or !event instead of on-event. I like that you can read @click as "at click" but @CaptainCodeman might be right about ! being less ambiguous and less chubby. With thin, equally-wide prefix symbols, the ${substitutions} visually stand out more.

Writing the name with no symbol carries the message that attributes are "raw" like natural HTML. As others have pointed out, it is good to avoid the incorrect impression that the event handlers are natural HTML.

Overall, I love the direction. I do sympathize with others who think there are a lot of symbols, but if you are giving up the double meaning of $ then it may be a fair trade-off to add another symbol for events. As someone who has not worked with lit-html very much, I find the third syntax very easy to understand.

@motss
Copy link

motss commented Jul 12, 2018

Using fancy symbols is kind of the way how Angular does this if I remember correctly. I'm comfortable with @justinfagnani 's idea.

html`<input
       id=${id}
       .value=${value}
       ?required=${required}
       @change=${onChange}>`

But then there is another question is that which symbols are much more appropriate to be used so that it makes sense to many of us. We might need a poll for that. I also have an idea like attribute we can probably use [value]=${value} which inspires by CSS selectors.

@bennypowers
Copy link
Collaborator

how would @change="${onChange}" affect #145 ?

@bennypowers
Copy link
Collaborator

Also, I've seen event names beginning with @ in the wild, would lit choke on those?

html`<my-el @@event="${onEvent}"></my-el>`

@KeithHenry
Copy link
Contributor

I like the prefixes, . works for properties, but @ instead of on- might be a little confusing for events. @ prefixes are already used in far too many places already.

@Buslowicz
Copy link

Buslowicz commented Jul 12, 2018

Personally I agree with most of the above (+ for everything, issues with @). I would argue about ! though as it is a boolean not and many people would confuse it. There are not many symbols though to be used and neither looks like "this is an event" out of the box. Also as @bennypowers pointed out, there might be events that start with @ and probably ones that start with any symbol, so dunno if that's an issue for us.
What you guys think about * for events, like <my-el *click="${listener}">? The only thing that comes to my mind when looking at * is wildcard, but that would mean every, so *click could translate to every click.

@KeithHenry
Copy link
Contributor

I like @Draccoz's idea, but there might be some overlap with generator function* syntax.

To me generators suggest RxJS/observable patterns, so repeated events with asyncAppend or asyncReplace.

I'm not sure whether that's a good thing or a potential confusion.

@Buslowicz
Copy link

Almost every symbol means something :D. Unless somebody suggests something really good, we will just have to pick the least confusing out of the confusing ones :(.

@kenchris
Copy link
Contributor

^change or ~change or :change ;-)

The latter is like assign to/subscribe to, so works for me

@Lodin
Copy link

Lodin commented Jul 12, 2018

@KeithHenry, since lit-html is a separate parser system, we shouldn't restrict its syntax with anything exists in JS. Even if * is taken for generators and @ for decorators, they can be used in lit-html because lit-html is not JavaScript

@Buslowicz
Copy link

@Lodin Can't agree with that. It may not be JS, but it's still closer to JS than funky templating systems (closer to JSX, which kinda still is JS). That means most of the people using lit (if not all) will be JS devs, and they should not be mislead by different meanings of the symbols they are used to.
However, we don't (and probably wont?) have generators/decorators passed like that in lit, so we may use either symbol. Still though, it should be as intuitive as possible.

@moebiusmania
Copy link

@Lodin if it's not JavaScript, then what it is? (don't say TypeScript 😄)

@idibidiart
Copy link

How about literal prop({someProp: val})and event({someEvent: fn}) ... this way you can have a map of props or events prop(propsMap) and event(eventsMap)

@idibidiart
Copy link

Naively speaking. Have yet to use lit-html and the ergonomics of proposed and existing syntax is after all just ergonomics, nothing that we can determine objectively. Maybe just make it user defined syntax with default being the ., ? and @ prefixes?

@KeithHenry
Copy link
Contributor

KeithHenry commented Jul 12, 2018

@Lodin that wasn't really my point - we can use pretty much anything we want, but ideally it should confuse devs used to JS syntax as little as possible. To me @ suggests users or decorators, ! means not, and * suggests subscription to an observer (rather firing an event that might be consumed by an observer).

We could make any of them work, but ideally we'd pick one that feels intuitive.

Personally I might go for + as I'm used to .NET's subscription syntax +=, so

html`<input
       id=${id}
       .value=${value}
       ?required=${required}
       +change=${onChange}>`

@recri
Copy link

recri commented Jul 12, 2018

You could go the opposite direction and make on-event the syntax you keep, so:

in-property=${valueForPropertyOnly}
if-attribute=${booleanForExistenceOfAttributeAndProperty}
on-event=${eventHandlerToBeAdded}
attribute=${valueForAttributeAndProperty}

Hyphenated attribute names already raise a flag when reading, and polymer users are already familiar with the on- prefix, even if I'm still confused. Progress on a learning curve is a terrible thing to waste.

Also, you won't be struggling to find the right single character for:

set-property=${writeTraceForProperty}
get-property=${readTraceForProperty}

whenever they or other proposals come up.

@KeithHenry
Copy link
Contributor

@recri That's going to be fun for a lot of existing web components. Most actively avoid on-event or (React style) onEvent, but there will be lots of in-property/inProperty and if-property/ifProperty attributes out there.

on- is an existing convention, but for maximum interopability new prefixes need to be invalid in setAttribute calls.

@Buslowicz
Copy link

@KeithHenry for those used to .NET + might feel convenient, for me it sums numbers or concats strings :P.
@recri That convention would bring even more confusion, like in-name="${userName}" or if-age="${userAge}", doesn't feel natural, especially the latter feels like it's a question about the property/attribute itself, not the value passed to it. The set-property looks better, but would confuse people with setAttribute.
To sum up: I have never ever seen a framework or library that uses verbose prefix to indicate property. True that Polymer community is used to on-event, but they are also used to attribute$ :P.

@bahrus
Copy link

bahrus commented Jul 13, 2018

I'd like to restate my argument for why I think it's better to put the property modifiers at the end of the property name rather than the beginning:

  • It is argued that they stand out more by being at the beginning. I would argue that we want these distinctions, especially between attributes, object/string properties, boolean attributes, to be subtle, not jarring. Yes, technically they are important, but starting off with a strange symbol would detract from the more important name of the property. Having them at the beginning looks more -- nerdy? Kind of like regular expressions, which tends to scare developers away

  • I think ergonomically it is better to be able to type a word for a property, then lift one's hands and deal with all the characters off the main typing area in one go, rather than two.

@bahrus
Copy link

bahrus commented Jul 13, 2018

I'm thinking events should be different. After all, much has been made of "data flows down via props, up via events". So making events look different ties into that. Maybe events should come at the beginning. I'm thinking of the css hover: a:hover.

So my new revised proposal is:

html`<input id=${id} value:=${value} required?=${required} :change=${onChange}>`

Not all the pseudo selectors are event related, but many are (sort of): :focus, :hover, :valid :visited :out-of-range :disabled :enabled :checked

Note that pascal and Go use := for assignment. In the case of Go, it allows you to not have to start with var (actually, I don't have that quite right -- implicit types or something. Never programmed in Go, sorry).

Update:

Evidently, Vue uses the colon prefix to represent attributes (vs properties). Glimmer seems to use @ to represent properties, btw. Anyway, the precedent set by Vue could cause Vue users to find the colon prefix confusing for defining events. Another syntax that has appeal to me:

html`<input id=${id} value:=${value} required?=${required} change::${onChange}>`

inspired by Rust (sorry, @CaptainCodeman :-) )

on- does appeal to me also, fwiw.

If we are trying to attract react/preact users, they would be most comfortable seeing onChange, but the important issue has been raised if you want an event to really start with an uppercase letter, since react/preact uses the upper case to make it more readable, but changes to lower case (I think) under the hood. Could both on and on- be supported, and one would use the latter if needing upper case letters, like on-URLChanged?

@CaptainCodeman
Copy link

I don't think what any other language (including JS) uses for symbols really matters. This is a DSL to generate HTML templates so that should really be the main focus, not how any symbol is used or what it means elsewhere - so many languages, all different.

Having the attribute syntax align with HTML attribute usage is definitely an improvement as part of that. We just want to come up with a clear and simple way to indicate properties and events.

@MaKleSoft
Copy link

MaKleSoft commented Jul 13, 2018

I like

html`<input id=${id} .value=${value} ?required=${required} @change=${onChange}>`

Although I don't understand why we need the ? syntax at all. I'm not familiar with the internals of the package but can't we just check the value type and behave accordingly? The logic could look something like this:

if (val === true) {
  el.setAttribute(attr, "");
} else if (val === false) {
  el.removeAttribute(attr);
} else {
  el.setAttribute(attr, val);
}

If you really want to bind the string representation of the boolean you can still do that explicitly by wrapping it in quotes. I.e.:

html`<div active=${true}></div>`

would render as

<div active></div>

While

html`<div active="${true}"></div>`

would result in

<div active="true"></div>

This is much closer to what I would expect coming in unbiased (if fact the second case is identical to what would happen in vanilla template literals) and since the whole point was to get rid of special syntax when binding to attributes this is a logical step imo.

One could go even further and define special behavior for promises, generators etc as well all of which can be optet out of by simply using quotes.

Finally, I think the prefix should only specify the target, while the interpretation of the value could be consistent across targets. I.e. promises could be handled identically for properties, attributes and even event handlers (of course in the latter case the promise would have to resolve to a function). If the expression resolves to something that isn't valid for a given target (e.g. a boolean for an event handler) you'll get a nice, explicit error that tells you exactly what you did wrong.

@Lodin
Copy link

Lodin commented Jul 13, 2018

@KeithHenry, @Draccoz, @moebiusmania, I suppose, @CaptainCodeman said everything I wanted in the better way. I believe if lit-html syntax is restricted basing on any other language it would harm lit-html's expressiveness. Moreover, I cannot imagine how decorator's @ could mix up with event's @: they are completely different. It's almost like comparing twitter's @ to JS decorator's @.

Anyway, I would vote for the following solution:

html`<input id=${id} .value=${value} ?required=${required} @change=${onChange}>`

IMO, it is very expressive and consistent solution.

BTW, why not to create poll anywhere, e.g. in twitter?

@Buslowicz
Copy link

I do agree with @Lodin and @CaptainCodeman, and my only issue with @ are mentions on slack (would have same issues to # :P). Other than that, @ does look like the best candidate for me, as it kinda is synonym for on (like @bennypowers pointed out).
Over all while still having personal favorites, I would be happy with either symbol.
Besides of events, I think most of us agrees for the rest?
@MaKleSoft Personally™ I would prefer to avoid things like auto magic inside lit. If we had lit to decides what to do, we may end up with use cases where it does not what we expect it to and sometimes it might be hard to fix.

@Lodin
Copy link

Lodin commented Jul 13, 2018

@Draccoz, regarding Slack: if you use triple backtick block for code (or snippet which is even better), it won't create any mentions. And you have to use this block anyway because if you don't Slack will highlight all your string between html's backticks (and still won't create mention :) ).

@Buslowicz
Copy link

I know that very well, kept reminding people about it all the time (sometimes I get tired of it though and ignore it...). We - slackers - are aware of it, but hell-of-a-lot of newcomers either don't know it or don't care about it, this is the potential source of infinite mentions (EmpressPolymer and couple of others know it very well since Polymer moved to npm with its own namespace ^^).

@MaKleSoft
Copy link

@Draccoz Not sure how my proposed solution would be any more magic than what lit already does. Can you elaborate?

@Buslowicz
Copy link

In the current proposal, you can clearly see what is happening, if there is a ? in front, it will definitely be a boolean, if . it will definitely be a property, etc. In your approach it's unclear what is going to happen, if somebody accidentally assigns 1 instead of true to the property (or the other way around by for example negating the value with !), it's gonna change the behavior, which shouldn't happen. This may be easier in TypeScript (which hints you types etc), but with plain JavaScript it may end up different than desired, and a lot of devs still prefer plain JS over TS.

@KeithHenry
Copy link
Contributor

@Draccoz @Lodin Yeah, avoiding @ for TS decorators is just a confusion thing, but since @polymer/lit-element is planning to support decorators we should consider those users.

However, the @ for usernames is just a personal gripe - we're going to be sharing lots and lots of code on forums like Twitter, Slack and GitHub that treat @ as a username. Yeah, you can work around that, it's something I find annoying.

@justinfagnani
Copy link
Collaborator Author

Thanks for all the fruitful discussion everyone!

I haven't said much yet, because so far there's really good reasoning going on and it's very interesting to just see how people think and feel about this and how that even changes rather quickly based on new comments. I definitely missed an opportunity to spread lots of 🚲and 🏠emoji throughout this issue!

I wanted to drop a couple of responses, though, maybe most importantly this:

from @Lodin:

BTW, why not to create poll anywhere, e.g. in twitter?

Mainly, because that might give the impression that the poll winner should "win". There are a lot of inputs to this decision, like how people feel, how intuitive it is, how familiar it is to other syntaxes, how easy it is to parse, consistency, etc. It's not a fully democratic decision. Even if it were, we all know how accurate internet polling is, and who knows what fraction of the future user-base the poll would actually reach.

And finally, because this is at least in part a subjective decision, as is all bike-shedding, there's an element of taste and even tastemaking here. We're going to take everything into consideration and try to make a decision that makes a cohesive whole.

What's really interesting here is that this is a decision just for the default syntax. The unopinionated core will remain, and it's very easy to make your own syntax. This is the entirety of the Option 3 syntax implementation:

export const partCallback =
    (instance: TemplateInstance,
     templatePart: TemplatePart,
     node: Node): Part => {
      if (templatePart.type === 'attribute') {
        const name = templatePart.name!;
        const prefix = name[0];
        if (prefix === '.') {
          return new PropertyPart(
              instance, node as Element, name.slice(1), templatePart.strings!);
        }
        if (prefix === '@') {
          return new EventPart(instance, node as Element, name.slice(1));
        }
        if (prefix === '?') {
          return new BooleanAttributePart(
              instance,
              node as Element,
              name.slice(1),
              templatePart.strings!);
        }
      }
      return defaultPartCallback(instance, templatePart, node);
    };

Now, we probably don't want a massive proliferation of syntaxes out there, but if some company, major widget library, or framework that uses lit-html wants to have a different opinion here to use across their templates, they can. @bgotink already has an Angular-inspired syntax which has quite a few additional features: https://github.com/bgotink/lit-html-brackets

Now some specific points:

One way to do something

I didn't mention this before, but I really want there to be one and only one way to do something. Since we can't do anything but set attributes when there's not a binding, this would require that the default binding is to attributes and that there is no other attribute syntax. So no :foo or similar for attributes.

LHS vs RHS positioning

For properties and events I'm fairly convinced that the LHS is the right position, so that it's clear at a glance that these are not properties. For boolean attributes I could go either way, since it is an attribute and the ? just modifies how it behaves, but consistency and avoid the symbol jumble are pretty strong arguments to me.

Events: @ vs !

Personally ! looks too much like "not" or "important". Of course @ has several meanings in different web-related contexts, but somehow it looks relatively "right" to me here, and phonically sounds almost event related, as pointed out by @bennypowers.

I just looked and Vue's shortcut for events is @, so that's an relevant precedent. There's even a proposal to add a .foo shorthand for properties, so overall we'd align pretty closely with Vue.

Automatic property setting

This is pretty well ruled out, but I haven't discussed why in this repo yet. There are two big problems here:

  1. We greatly prefer explicit over implicit. I think reading templates and knowing whether an attribute or property is set without having to know about the implementation of the element is useful. Sometimes the automatic choice won't be correct, and then you need syntax to force either attributes or properties, and then you have two ways to do both - automatic and explicit. I think all together it ends up being confusing. I know Preact does this, but that's mostly because they don't have control of syntax in JSX to be explicit.

  2. We want to be compatible with upgrades. Pre-upgrade an element won't have many properties defined, so they'd be set as attributes, and then hopefully pulled in correctly by the element when it does upgrade. This won't work correctly for complex objects though.

@justinfagnani
Copy link
Collaborator Author

Thanks for all the input everyone!

#398 is merged now. We decided to go with option 2:

html`<input id=${id} .value=${value} ?required=${required} @change=${onChange}>`

Note that lit-extended.js hasn't changed, so the next release should be non-breaking, but it is deprecated.

cc @bgotink who maintains a syntax

@daKmoR
Copy link
Contributor

daKmoR commented Jul 25, 2018

does this also support setting properties if just provided as a string

html`<my-el .prop="some-string">`

@justinfagnani
Copy link
Collaborator Author

@daKmoR No. lit-html doesn't process attributes that don't have bindings. Supporting that might have a perf impact, and would require some API changes. Let me think about this for a little bit...

@ruphin
Copy link
Contributor

ruphin commented Jul 25, 2018

This syntax does exactly what you want, without any performance overhead:

html`<my-el .prop=${"some-string"}>`

I think it's much easier to explain that lit-html interacts with and only with instances of ${ ... } in your template. Implementing syntax like the property binding suggested by @daKmoR violates that expectation, which weakens the abstraction.

It's a slippery slope. Property setters through attributes are ok, but data bindings are not? It'll just become an arbitrary cutoff point for what 'feels right'. The cutoff for what lit-html interacts with should be ${ ... } which is clear and non-arbitrary.

@Buslowicz
Copy link

I totally agree with @ruphin, lit only interacts with template string intersections, the rest is a 100% standard html behavior. If we start messing with those, we end up with a library being far away from the standards (and we aimed at it didn't we?).

@klebba
Copy link

klebba commented Sep 6, 2018

@justinfagnani in lit-extended we could bind an attribute like <a target$="${foo}">link</a> and the $ would influence the binding such that when foo is undefined, the target attribute would not be stamped to the DOM. With the changes in #398 this functionality appears to be gone -- am I missing something here? Thanks!

@justinfagnani
Copy link
Collaborator Author

@klebba that was actually a bug that ignored undefined for the initial render, but not subsequent renders. Fixing that was not related to the syntax change, but was probably released at the same time.

We've since added the ifDefined directive, which can be used like so:

import {ifDefined} from 'lit-html/directives/if-defined.js';

html`<a target="${ifDefined(foo)}">link</a>`

@klebba
Copy link

klebba commented Sep 6, 2018

@justinfagnani thanks for the prompt reply! Would be convenient to be able to accomplish this using template syntax, e.g. <a +href="${foo}" +target="${bar}"> but ifDefined should definitely do the trick for our migration from 0.9 to 0.11.1

@aadamsx
Copy link

aadamsx commented Sep 7, 2018

Would be convenient to be able to accomplish this using template syntax, e.g. <a +href="${foo}" +target="${bar}">

Agree 100%

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

Successfully merging a pull request may close this issue.