Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@justinfagnani
Copy link
Collaborator

@justinfagnani justinfagnani commented Jun 15, 2021

This needs more tests and I'm not entirely happy with using the private object this way, but the general approach works and is small.

Tests needed:

  • - AsyncDirective lifecycle in all binding types
  • - Multiple spread directives on an element
  • - Removing the spread directive
  • - Collisions between spread and normal bindings

@github-actions
Copy link
Contributor

github-actions bot commented Jun 15, 2021

πŸ“Š Tachometer Benchmark Results

Summary

Results

tachometer-reporter-action v2 for Benchmarks

readonly [name: string]: unknown;
}

class SpreadDirective extends Directive {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider whether this should be an AsyncDirective so that its disconnected callback can undo all the parts if you render a different value to the expression (or whether spread must be static), e.g....

<div ${bool ? spread({foo, bar}) : nothing}></div>

To be fair, we don't do a good thing for e.g. classMap in this situation either (it would also need to be disconnectable), which seemed a bit overkill given the performance hit:

<div class=${bool ? classMap({foo, bar}) : nothing}></div>

Copy link
Collaborator Author

@justinfagnani justinfagnani Jun 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this is the most complete implementation, but given that it's more costly it might be better to keep things simple. I think this pattern should suffice:

html`<div ${spread(bool ? {foo, bar} : undefined)}></div>`

}
}

render(_values: SpreadValues) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can imagine use cases where it'd be nice to let values be nullable, to avoid users from needing to always make a throwaway object in the alternate case:

<div ${spread(maybeGotStuff ?? {})}></div>

or

<div ${spread(shouldRenderStuff ? stuff : {})}></div>

Again, we don't do this in classMap/styleMap, but I think it's more likely to have patterns where entire bags are forwarded in (e.g. "formControlAttributes") vs. being generated by the user (otherwise the user could have just written the attribute bindings themselves).

But it does sort of beg the question what we expect spread to actually be used for in the OSS world.

Copy link
Collaborator Author

@justinfagnani justinfagnani Jun 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, as per the other thread.

I imagine this form of spread to be a lower level thing that's often wrapped for better DX. An element might receive a bag of property or event bindings and need to add the correct prefix. attr will wrap this. Custom attribute-merging strategies will... do something. We need to make sure that's possible.

}

render(_values: SpreadValues) {
// TODO for SSR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any nascent ideas here? Seems like we could go two ways here:

  • Return an array of descriptors of the part types + values, and figure out how to reuse the attribute-part opcode handling to handle the per-part-specific stuff:
    case 'attribute-part': {
  • Return the array of acutal part instances (iow, use the update code) that the attribute-part opcode handling can directly use, and figure out how to deal with the fact that you don't have an actual part to get the element to pass in render (which isn't technically a problem in SSR because it doesn't need the element).

We do need to reuse code in the attribute-part opcode handling to ensure directives passed to spread work correctly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure yet. I think something like just forwarding the object in an AttributeResult type might work, even if it's a bit weird to explain how that works or doesn't client-side.

I think figuring out hydration is the hard part here...

@LarsDenBakker
Copy link
Contributor

How do you feel about having a directive which spreads only properties, and one that spread binding like the current implementation? A very common use case is spreading an object of properties, often coming from some other source. Right now users would need to always transform the object to have all keys start with a .. Or they won't notice and always set attributes, which isn't great.

@justinfagnani
Copy link
Collaborator Author

justinfagnani commented Jun 16, 2021

@LarsDenBakker

I think that's a good idea, but hopefully can be well expressed as a wrapper on this:

spreadProps = (props) => spread(Object.fromEntries(Object.entries().map(([k, v]) => ['.' + k, v])));

If the Object.fromEntries(Object.entries()..) bit is too much work we can look at customizing the name->part mapping with a callback.

@LarsDenBakker
Copy link
Contributor

@justinfagnani that wrapper would do quite a lot of work each render though, so would be nice to make that more convenient.

@abdonrd
Copy link
Contributor

abdonrd commented Aug 20, 2021

Is there news on this? Thanks in advance!

The Carbon team is interested in use it:
carbon-design-system/carbon-web-components#657 (comment)

It's one of the main things they are waiting to update to Lit v2.

cc @jeffchew

@justinfagnani justinfagnani modified the milestones: Lit RC.next, Lit 1.1 Aug 20, 2021
@changeset-bot
Copy link

changeset-bot bot commented Nov 3, 2021

⚠️ No Changeset found

Latest commit: 501fca4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@justinfagnani justinfagnani changed the title [lit-html] Add spread directive [lit-html] Add spread directive and attr templates Nov 3, 2021
@justinfagnani justinfagnani marked this pull request as draft November 4, 2021 00:21
@daKmoR
Copy link
Contributor

daKmoR commented Jan 10, 2022

any chance this is going to happen within January 2022?

we are thinking about waiting or hacking our own version for now πŸ˜…

@samanime
Copy link

samanime commented Jan 12, 2022

It'd be great to have this feature soon. I had upgraded without realizing it was missing and now I'll need to downgrade back.

If figuring out how to be deterministic when there are multiple objects with duplicate keys, why not simply limit it to a maximum of one spread operation per element and let them merge objects before. Then you don't have to try and come up with a deterministic approach for everyone, as they can solve it beforehand themselves.

This PR was started back 7 months ago and we still don't have a solution. =S

@songkeys
Copy link

It's been a whole year. What is the current status of this feature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants