-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Add spread for attributes #280
Conversation
So I just pushed a commit that uses a component with attribute spread and it worked fine.. I might be confused on what constitutes a component because I thought I'd have to implement the Spread attribute inside of |
@@ -11,6 +11,10 @@ export default { | |||
let openingTag = `<${node.name}`; | |||
|
|||
node.attributes.forEach( attribute => { | |||
if ( attribute.type === 'Spread' ) { | |||
openingTag += ` \${Object.keys( root.${attribute.name} ).map( prop => \`\${prop}="\${root.${attribute.name}[prop]}"\` ).join( ' ' )}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs some kind of escaping. But then again, the normal attribute code below has no escaping either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this code was written a little later than the generator code and while it works, I think it's really sloppy.
@@ -22,6 +22,20 @@ export function teardownEach ( iterations, detach, start ) { | |||
} | |||
} | |||
|
|||
export function spreadAttributes ( node, obj ) { | |||
if (obj !== null && typeof obj === 'object') { | |||
for ( var property in obj ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the policy on browser compatibility?
I personally prefer iterating over Object.keys instead of for-in + hasOwnProperty.
Also, I would personally prefer an early return instead of indenting, but that is a matter of taste :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow I for some reason thought Object.keys was ES6, but it's actually ES5! Definitely will use that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and on browser compatibility, the only browser with significant market share that doesn't support ES5 is IE8 at 3% marketshare. But I honestly think that 8 years later we can ship ES5 features to browsers. + there's plenty of polyfills for ES5 at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's reasonable to expect Object.keys
to exist, especially since it's easily polyfilled if you absolutely have to support IE8 or whatever — though a for-in
is quicker, no?
(Related: the compiler generates code with Object.assign
which is less widely-supported than Object.keys
— again, can be easily polyfilled but I wondered if there should be an internal assign
helper instead.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to this Object.keys + for loop is faster: https://jsperf.com/object-keys-vs-for-in-with-closure/3
Added support for updates and components in PaulBGD#1, which is based on this branch. Having said that, how strongly do you both feel about this addition? I can't help but wonder if it's a bit of a footgun, since it means less explicit templates and reduced static analysis ability, which feels like it might be a slippery slope (e.g. could potentially impact future optimisations?). Isn't the analogous |
Not that I know of. For some you want to use object spread/rest to filter out the props you do not want to pass to the child. Otherwise spread in react is perfectly fine. |
So one of the issues that I noticed was that being dynamic means that you don't know what attributes are being passed. Since the common use-case for this is for passing multiple arguments from data, I wonder if we could do something where we specify the properties of data that should be passed to the child. One thing we could do is a syntax like
Where it passes values from the parent, but that's a little verbose still. I suppose the best way to statically analyze the object to make this PR work is to specify the parameters.
and that's the easiest way I can think of making it statically analyzable as-is. |
Updating and component-friendly spread attributes
Any additional thoughts on this? One thing suggested if we're sure we want to go the dynamic route is allow passing all data. |
To be totally honest I'm still not totally convinced of the utility of this change — if we're talking about things like adding a |
When I use svelte and I want to have custom styles for buttons or special effects for them, I create a class called ButtonComponent or ButtonWrapper. How can I pass |
Isn't that just this? <!-- Main.html -->
<ButtonComponent type='reset' name='a button!'>some label</ButtonComponent> <!-- ButtonComponent.html -->
<button type='{{type}}' disabled='{{disabled}}' name='{{name}}' value='{{value}}'>
{{yield}}
</button>
<script>
export default {
data: () => ({
type: 'button',
disabled: false,
name: '',
value: ''
})
};
</script> In other words, as a component author I think it makes sense that you have to anticipate the different attributes that could get passed, otherwise you could wind up with some unpredictable behaviour. If it's the <button :type :disabled :name :value>{{yield}}</button> ...where Have also wondered about replacing <button type={type} disabled={disabled} name={name} value={value}>{yield}</button> |
I think I agree and I like the |
My original proposal in #195 was partly tailored towards iteration with components. Since |
What if it did support destructuring? {{#each items as { description, completed } }}
<Item :description :completed/>
{{/each}} Granted, there's a lot of curly braces there, and I haven't thought through the ramifications of this at all, but does that help solve the problem? |
It does yes. You still have to enumerate all the props though, but that might as well force the author to think more about it and come up with a solution that does not rely on tons of props. |
I'm going to close this since it conflicts with the main branch and because doing dynamic spreading of attributes would make things like #310 not possible. |
I feel bad necroing this PR, but I'd like to raise a voice in favor of spread operators. Without them, I have to add hacky I didn't think spread properties were an anti-pattern in React - maybe some people avoided them, but I found them very useful in making general components. |
The big issue I've seen with having a spread all (which I think is what most people want, with the ability to override certain attributes) is that Svelte doesn't know at compile time if the component will be used by the DOM and/or by another component. This means we have to pass all the attributes with an object at runtime, which incurs a slight cost. I think that the feature is useful though, especially if we do want to support aria attributes. Right now it's very annoying to proxy all the events a node can have, as well as passing |
I'm running into this exact issue with a lib of mine (https://github.com/bestguy/sveltestrap) where I wrap of Bootstrap components as Svelte components, and it's really, really difficult to manually specify the |
This will close #195.
It implements spread as:
into
where options is:
This is a work in progress until I've finished the implementation for Components.
Some things that might need to be discussed:
_
to spread all data, which would look like{{_}}
. Is this the syntax we want to use?