Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reusable Alpine.js components?

How do I use Alpine.js to create a reusable component and display it? E.g., maybe I want to define an generic Alpine.js button component that changes text and color from parameters, then have my Alpine.js navbar component use the button component to show a login button.

Can I do this in pure client-side code, without relying on a server templating out all of the button HTML everywhere the button component is used?

like image 272
spiffytech Avatar asked Dec 23 '22 16:12

spiffytech


2 Answers

Can I do this in pure client-side code, without relying on a server templating?

Yes, you can.

Alpine.js always will try to persuade you to use a server side templating engine.

But just like you, I don't let myself be persuaded:

<template x-component="dropdown">
    <div x-data="{ ...dropdown(), ...$el.parentElement.data() }">
        <button x-on:click="open">Open</button>

        <div x-show="isOpen()" x-on:click.away="close" x-text="content"></div>
    </div>
</template>

<x-dropdown content="Content for my first dropdown"></x-dropdown>

<div> Random stuff... </div>

<x-dropdown content="Content for my second dropdown"></x-dropdown>

<x-dropdown></x-dropdown>

<script>
    function dropdown() {
        return {
            show: false,
            open() { this.show = true },
            close() { this.show = false },
            isOpen() { return this.show === true },
            content: 'Default content'
        }
    }

    // The pure client-side code
    document.querySelectorAll('[x-component]').forEach(component => {
        const componentName = `x-${component.getAttribute('x-component')}`
        class Component extends HTMLElement {
            connectedCallback() {
                this.append(component.content.cloneNode(true))
            }
 
            data() {
                const attributes = this.getAttributeNames()
                const data = {}
                attributes.forEach(attribute => {
                    data[attribute] = this.getAttribute(attribute)
                })
                return data
            }
        }
        customElements.define(componentName, Component)
    })
</script>
like image 179
lovedder1995 Avatar answered Jan 05 '23 01:01

lovedder1995


Alpine.js contributer @ryangjchandler remarks that reusable templates are out of scope for Alpine.js:

The proposed [Alpine.js version 3] x-component directive will NOT have anything to do with templating or the markup for your component. Instead it will provide a way of writing more immediately reusable data sets & functions, whilst reducing the amount of directives you need to define in your markup.

If you need re-usable templates, I would consider using a server-side template engine or a more monolithic front end framework such as Vue or React. (link)

and

The functionality you are looking for is far out of the scope of Alpine. It's designed to work alongside your existing markup from the server or static files, not replace / component-ise your markup. (link)

like image 21
spiffytech Avatar answered Jan 05 '23 00:01

spiffytech