[英]Reusable Alpine.js components?
如何使用 Alpine.js 创建可重用组件并显示它? 例如,也许我想定义一个通用的 Alpine.js 按钮组件,它根据参数更改文本和颜色,然后让我的 Alpine.js 导航栏组件使用该按钮组件来显示登录按钮。
我可以在纯客户端代码中执行此操作,而不依赖服务器在使用按钮组件的所有地方模板化所有按钮 HTML 吗?
我可以在不依赖服务器模板的情况下使用纯客户端代码执行此操作吗?
是的你可以。
Alpine.js 总是会尝试说服您使用服务器端模板引擎。
但就像你一样,我不让自己被说服:
<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>
Alpine.js 贡献者@ryangjchandler 评论说,Alpine.js 的可重用模板超出了 scope:
提议的 [Alpine.js version 3] x-component 指令与组件的模板或标记没有任何关系。 相反,它将提供一种编写更立即可重用数据集和函数的方法,同时减少您需要在标记中定义的指令数量。
如果您需要可重复使用的模板,我会考虑使用服务器端模板引擎或更单一的前端框架,例如 Vue 或 React。 ( 链接)
和
您正在寻找的功能远远超出了 Alpine 的 scope。 它旨在与服务器或 static 文件中的现有标记一起工作,而不是替换/组件化您的标记。 ( 链接)
使用 Alpine.js v3 和 Global Alpine Components,您可以使用 Alpine.component() 来封装此功能。
<div x-data="dropdown">
...
</div>
<script>
Alpine.component('dropdown', () => ({
open: false,
toggle() { this.open = !this.open }
}))
</script>
使用alpinejs-component
cdn 的同一页:
<div
x-data="{
people: [
{ name: 'John', age: '25', skills: ['JavaScript', 'CSS'] },
{ name: 'Jane', age: '30', skills: ['Laravel', 'MySQL', 'jQuery'] }
]
}"
>
<ul>
<template x-for="person in people">
<!-- use the person template to find the <template id="person"> element. -->
<x-component-wrapper x-component template="person" x-data="{ item: person }"></x-component-wrapper>
</template>
</ul>
</div>
<template id="person">
<li class="user-card">
<h2 x-text="item.name"></h2>
<p x-text="item.age"></p>
<ul>
<template x-for="skill in item.skills">
<li x-text="skill"></li>
</template>
</ul>
</li>
</template>
<script src="https://unpkg.com/alpinejs-component@1.x.x/dist/component.min.js"></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
使用 url 导入 html 模板:
<div
x-data="{
people: [
{ name: 'John', age: '25', skills: ['JavaScript', 'CSS'] },
{ name: 'Jane', age: '30', skills: ['Laravel', 'MySQL', 'jQuery'] }
]
}"
>
<ul>
<template x-for="person in people">
<x-component-wrapper x-component url="/public/person.html" x-data="{ item: person }"></x-component-wrapper>
</template>
</ul>
</div>
<script src="https://unpkg.com/alpinejs-component@1.x.x/dist/component.min.js"></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
person.html
:
<li class="user-card">
<h2 x-text="item.name"></h2>
<p x-text="item.age"></p>
<ul>
<template x-for="skill in item.skills">
<li x-text="skill"></li>
</template>
</ul>
</li>
通过 npm 安装:
npm i -D alpinejs-component
yarn add -D alpinejs-component
注册插件:
import Alpine from "alpinejs";
import component from "alpinejs-component";
Alpine.plugin(component);
window.Alpine = Alpine;
Alpine.start();
或者在浏览器中使用模块:
<x-component-wrapper x-component template="dropdown" x-data="dropdown"></x-component-wrapper>
<x-component-wrapper x-component template="dropdown" x-data="dropdown"></x-component-wrapper>
<template id="dropdown">
<div @click="close" class="dropdown-toggle">
<button x-on:click="open">Open</button>
<div x-show="show" x-text="content"></div>
</div>
</template>
<script type="module">
import { default as Alpine } from 'https://cdn.skypack.dev/alpinejs'
import alpinejsComponent from 'https://cdn.skypack.dev/alpinejs-component'
function dropdown() {
return {
show: false,
open() {
console.log('open')
this.show = true
console.log(this.show)
},
close(event) {
const button = this.$el.querySelector('button')
const target = event.target
if (this.$el.contains(target) && !button.contains(target)) {
this.show = false
}
},
get isOpen() {
return this.show === true
},
content: 'Default content',
init() {
console.log(this.$el.parentElement)
console.log('dropdown --- init')
},
}
}
Alpine.data('dropdown', dropdown)
Alpine.plugin(alpinejsComponent)
Alpine.start()
</script>
工作得很好。
您可以使用Alpine.data
和记录的方法来使用x-bind
封装指令。 诀窍是绑定x-html
指令。 在您的 HTML 中执行此操作:
<div x-data="dropdown" x-bind="bind"></div>
在您的 Javascript 中:
document.addEventListener('alpine:init', () => {
Alpine.data('dropdown', () => ({
show: false,
bind: {
['x-html']() { return `
<button @click="show = !show">Click me!</button>
<div x-show="show">Hello World</div>
`},
},
}));
})
这有点 hacky,因为您将所有嵌套内容封装在x-html
指令中绑定的多行 HTML 字符串中(尽管可能不比到处克隆模板的替代方案更 hacky)。 确保您没有在内容中使用反引号字符。 然而,内容可以嵌套到你喜欢的深度,并且可以包含 Alpine.js 指令。 您可以通过声明参数并将参数传递给Alpine.data
来初始化您的组件。 您还可以绑定x-modelable
以将组件的任何属性公开为输出。
如果您更喜欢使用模板,可能是因为当标记未嵌入字符串时您的编辑器可以更好地突出显示语法,您可以将此方法与模板结合使用。 这是一个演示x-modelable
和模板使用的示例。 实际上,Alpine 会为您克隆模板。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.js"></script>
</head>
<body>
<div x-data="{clicked: false}">
<div>Clicked is <span x-text="clicked"></span></div>
<div x-data="dropdown" x-bind="bind" x-model="clicked"></div>
</div>
<template id="dropdown">
<button @click="show = !show">Click me!</button>
<div x-show="show">Hello World</div>
</template>
</body>
<script type="text/javascript">
document.addEventListener('alpine:init', () => {
Alpine.data('dropdown', () => ({
show: false,
bind: {
['x-modelable']: 'show',
['x-html']() { return document.querySelector('#dropdown').innerHTML},
},
}));
})
</script>
</html>
Vimesh UI ( https://github.com/vimeshjs/vimesh-ui ) 中带有原生自定义元素的 x-component 是一个更完整的可重用组件实现:
<head> <script src="https://unpkg.com/@vimesh/style" defer></script> <script src="https://unpkg.com/@vimesh/ui"></script> <script src="https://unpkg.com/alpinejs" defer></script> </head> <body x-cloak class="p-2" x-data="{name: 'Counter to rename', winner: 'Jacky'}"> Rename the 2nd counter: <input type="text" x-model="name" class="rounded-md border-2 border-blue-500"> <vui-counter x-data="{step: 1}":primary="true" title="First" x-init="console.log('This is the first one')" owner-name="Tom"></vui-counter> <vui-counter x-data="{step: 5}":title="name + ' @ ' + $prop('owner-name')" owner-name="Frank"></vui-counter> <vui-counter x-data="{step: 10, value: 1000}":owner-name="winner"> <vui-counter-trigger></vui-counter-trigger> </vui-counter> <template x-component.unwrap="counter":class="$prop('primary')? 'text-red-500': 'text-blue-500'" x-data="{ step: 1, value: 0}" x-init="$api.init && $api.init()" title="Counter" owner-name="nobody"> <div> <span x-text="$prop('title')"></span><br> Owner: <span x-text="$prop('owner-name')"></span><br> Step: <span x-text="step"></span><br> Value: <span x-text="value"></span><br> <button @click="$api.increase()" class="inline-block rounded-lg bg-indigo-600 px-4 py-1.5 text-white shadow ring-1 ring-indigo-600 hover:bg-indigo-700 hover:ring-indigo-700"> Increase </button> <slot></slot> </div> <script> return { init() { console.log(`Value: ${this.value}, Step: ${this.step}`) }, increase() { this.value += this.step } } </script> </template> <template x-component="counter-trigger"> <button @click="$api.of('counter').increase()" class="inline-block rounded-lg mt-2 bg-green-600 px-4 py-1.5 text-white shadow ring-1 ring-green-600 hover:bg-green-700 hover:ring-green-700"> Tigger from child element</button> </template> </body>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.