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

Magics: Introduce new $mixin magic helper #4200

Closed

Conversation

ryangjchandler
Copy link
Contributor

This pull request introduces a new $mixin magic helper that aims to make components more easily composable.

At the minute, if you want to use multiple data providers for a component, you have to do something like this:

<div x-data="{
    ...foo(),
    ...bar(),
}">

This doesn't feel very nice – plus it has some downsides such as init() and destroy() functions being overwritten by the final item in the spread.

The goals of the $mixin helper are:

  1. Make the API feel a little nicer to write.
  2. Handle the init() and destroy() functions correctly.

The above code would become something like this:

<div x-data="$mixin(foo, bar)">
</div>

If both foo() and bar() have an init(), $mixin() is smart enough to call both of those functions in the correct order (whichever order they're being mixed in). The same goes for destroy() methods.

Of course, conflicting properties in each function are still an issue but that's not really avoidable without some weird object nesting stuff and I'd say that's not very intuitive.

Either way, I think this is a nice API for core and something I'd find myself using a lot, especially given the nicer syntax!

Let me know what you think 🤞

@ekwoka
Copy link
Contributor

ekwoka commented May 8, 2024

This will break getters and setters.

To preserve them you would need more like

components.reduce((acc, obj) => {
        Object.defineProperties(acc, Object.getOwnPropertyDescriptors(obj));
        return acc;
      }, {})

instead of the spread operator.

Spreading will simply take the value out of a getter right then, and setters would be lost entirely.

I use the above (as a similar style of mixin) in production.

Also, fyi, if you define it as an Alpine.data instead of a Alpine.magic it can be then only used in the x-data directive, which is pretty cool.

Copy link
Contributor

@ekwoka ekwoka left a comment

Choose a reason for hiding this comment

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

Probably needs tests

packages/alpinejs/src/magics/$mixin.js Outdated Show resolved Hide resolved
@calebporzio
Copy link
Collaborator

Nice work, and good name for it. Can you give me some real-world examples to chew on before adding it? I'm leaning towards this being a good merge though. Also thanks for the notes @ekwoka

@ryangjchandler
Copy link
Contributor Author

Yep, you're right @ekwoka – completely forgot about the get and set thing with ...!

Will get that sorted and tested.

@ryangjchandler
Copy link
Contributor Author

Changes made r.e. get and set. Tests are all passing locally – can't see any failures in the Action output either, so maybe worker is timing out or it's flaky?

@joshhanley
Copy link
Collaborator

@ryangjchandler I've triggered tests to re-run

@calebporzio
Copy link
Collaborator

Does anyone have any compelling real-world examples of this they'd like to share? or links to discussions or other community evidence around this?

@cipriano200
Copy link

cipriano200 commented May 10, 2024

Few weeks ago i was doing an implementation for a wordpress premium form builder, had to create 3 custom form fields each of them are dependent on each other.

The issue was i didn't had control over form element(tag) so i couldn't do: <form x-data="component">

Mixing would solve this issue by mixing the 3 custom fields components

@ekwoka
Copy link
Contributor

ekwoka commented May 11, 2024

@calebporzio

Broadly, what this solves is just wanting to have the same root used for two (or more) components.

This can be implemented with 2 elements with each of the components (increases div soup), or with multiple x-data on the single element (can provide questionable issues of the cascade in the event of overlapping keys).

Those would be the two current ways to implement it without reaching for this kind of utility.

It's overall simple to implement if a dev needs to, though the case of lifecycle methods and getters/setters may be missed or cause some headache.

So it is another abstraction that does not NEED to be part of core, like some other functionality improvements might need to.

I've only used it a very small number of times, and use it less and less with newer stuff (leaning towards custom directives for more and more stuff), but it is something that many would likely want to do and not fully understand how to.

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 this pull request may close these issues.

5 participants