facebook / jsx

The JSX specification is a XML-like syntax extension to ECMAScript.
http://facebook.github.io/jsx/
1.96k stars 133 forks source link

Allow for . in attribute names #42

Open Alxandr opened 8 years ago

Alxandr commented 8 years ago

I would like if . was allowed in attribute names, like this:

<tag-name some.attribute="value" />

which would be transformed into (using react)

React.createElement("tag-name", { "some.attribute": foo });

It's allowed in HTML to have attributes with dots in them.

RReverser commented 8 years ago

What should

<tag-name some={{ a: 1 }} some.b=2 />

and

<tag-name some.a=1 some={{ b: 2 }} />

and

<tag-name some="1" some.b="2" />

do?

It brings too much questions, conflicts and undefined behavior IMO.

Alxandr commented 8 years ago

None of those are problematic. If you read my transformation, it should be like this:

React.createElement("tag-name", { "some": {a: 1}, "some.b": "2"});
React.createElement("tag-name", { "some.a": "1", "some": {b}});
React.createElement("tag-name", { "some": "1", "some.b": "2"});
Alxandr commented 8 years ago

Let me clarify some more. This are not object expressions/merging. It's just treating the entire attribute (dots and all) as a string. So you can't do attrs.some.a, you'll have to do attrs['some.a'].

sebmck commented 8 years ago

I don't think @RReverser was talking about the syntactic ambiguity rather that in JavaScript a . is used to represent property access and the use of it as a key is very confusing.

Alxandr commented 8 years ago

@sebmck well, he said "conflicts and undefined behavior". Of which there are none.

NekR commented 8 years ago

@Alxandr why underscore (_) does not work for you?

<div test_foo_bar="123"></div>

https://babeljs.io/repl/#?experimental=true&evaluate=true&loose=false&spec=false&code=%3Cdiv%20test_foo_bar%3D%22123%22%3E%3C%2Fdiv%3E

Alxandr commented 8 years ago

@NekR it's not at all that it doesn't work. It's that I have a HTML element that expects an attribute with . in it's name.

NekR commented 8 years ago

@Alxandr Hmm.. interestingly, why?

Alxandr commented 8 years ago

@NekR No particular reason. It was made that reason a good while ago. It's a custom element, and I've been using it for a while. Now, I could obviously change it, which is probably what I'll end up having to do (as opposed of today, where I customly do element.setAttribute). I just thought JSX should support this, mostly given that I figured it would be easy to implement, and not cause any issues with existing code. And HTML supports it.

NekR commented 8 years ago

@Alxandr yes, good reason. But there might be a problem with current <foo.Bar> which in supported implementations means foo.Bar() (or create(foo.Bar)) and if <div foo.bar=""> will translate just to {'foo.bar': ''} it may cause some confusions. Anyway, at some point I also thought that JSX should support it was confused that it don't. Interestingly what other people think here.

Alxandr commented 8 years ago

@NekR I'd argue that those are fairly different though. One is an attribute, the other is an element name. Though, I've already stated my opinion. So I'll let the rest discuss.

treshugart commented 8 years ago

Big +1 to this. I think the question worth asking is: is there benefit in not conforming to the HTML spec? And if so, does it outweigh the benefits that consumers get by having that predictability?

amiller-gh commented 7 years ago

Hey all, opening this old discussion back up! Just had a major use case come up in a new project that this language feature would be great syntactic sugar for.

Side note: while looking into this language feature, I found an inconsistency between the JSX spec and Babylon's implementation that more work on this issue may help resolve, but should probably be fixed regardless (excuse me for the length of this comment!)

The JSX parser in Babylon currently allows namespaced tag names with member expressions:

<test:foo.bar></test:foo.bar>

However, this is wrong according to the current JSX spec, where JSXNamespacedNames do not allow member expressions, only identifiers:

JSXElementName :
- JSXIdentifier
- JSXNamespacedName
- JSXMemberExpression

JSXAttributeName :
- JSXIdentifier
- JSXNamespacedName

JSXIdentifier :
- IdentifierStart
- JSXIdentifier IdentifierPart
- JSXIdentifier NO WHITESPACE OR COMMENT `-`

JSXNamespacedName :
- JSXIdentifier `:` JSXIdentifier

JSXMemberExpression :
- JSXIdentifier `.` JSXIdentifier
- JSXMemberExpression `.` JSXIdentifier

This grammar allows tag names like these:

<foo:bar></foo:bar>
<foo.bar></foo.bar>

But does not allow for namespaced member expression tags like the Babylon parser currently does:

<test:foo.bar></test:foo.bar>

By updating the definition of JSXNamespacedName to:

JSXNamespacedName :
- JSXIdentifier : JSXIdentifier
- JSXIdentifier : JSXMemberExpression
- JSXMemberExpression : JSXIdentifier
- JSXMemberExpression : JSXMemberExpression

The spec will match what Babylon currently parses as valid JSX, and will have the fringe benefit of enabling namespaced, member expression attributes:

<div attr:biz.baz="value"></div>

One more small update to JSXAttributeName and we get support for un-namespaced attribute names that @Alxandr asked for.

JSXAttributeName :
- JSXIdentifier
- JSXMemberExpression
- JSXNamespacedName

However, none of the above addresses @RReverser and @NekR's concern about behavior ambiguity. One option that addresses both use cases is to mirror the behavior used by tag names, and preserve the use of member expressions as data accessors, where:

let tagNames = { div: 'div' };
let attrsNames = { custom: "some.value" };
<tagNames.div attrsNames.custom="value" />;

may be transformed to

let tagNames = { div: 'div' };
let attrsNames = { custom: "some.value" };
React.createElement(tagNames.div, { [attrsNames.custom]: "value" });

My use case: a library that exports attribute names for use on elements that may change between dev and production builds and should be applied dynamically, but where you want to apply them in a cleaner, more statically analyzable way than with spread attributes:

import lib from "my-library";
return ( <div lib.customProp="value" ></div> );

I think theres value in adding this language feature, and I'm happy to help make updates to assorted parsers / transpilers to support it if the proposal moves forward – the example Babylon implementation I've been playing with is here: https://github.com/epicmiller/babylon/commit/de84728f7105c02b1661d823f221eda6ad7d8c83

syranide commented 7 years ago

My use case: a library that exports attribute names for use on elements that may change between dev and production builds and should be applied dynamically, but where you want to apply them in a cleaner, more statically analyzable way than with spread attributes:

return ( <div [lib.customProp]="value" ></div> ); is the answer to that (computed property names). Although I have no idea why changing attribute names between dev and prod is a good idea.

amiller-gh commented 7 years ago

Ah well look at that, I just re-created an existing proposal! Thanks for calling it out @syranide. Should this issue be closed in favor of #21?