使用 AlpineJS 创建布局和组件



我想知道 - 有没有办法使用 AlpineJS 创建布局和/或组件(以便我可以保持代码干燥(? 如果没有,是否有任何与 AlpineJS 集成的解决方案来添加此功能(而不必求助于 React 或 Vue 等完整框架(?

Alpine.js 试图尽可能避免模板化,因为它被设计为与服务器端模板或静态侧生成器协同工作。

根据 Alpine.js 文档加载 HTML 部分的示例是使用x-init="fetch('/path/to/my/file.html).then(r => r.text()).then(html => $refs.someRef.innerHTML = html)"(x-init只是可以执行此操作的一个位置(。

您可以在 vimesh UI 中使用带有命名槽的 x 组件

<head>
<script src="https://unpkg.com/@vimesh/ui"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body>
<vui-card>
<template slot="title">TITLE</template>
This is content
<template slot="footer">Copyright</template>
</vui-card>
<template x-component="card">
<h1>
<slot name="title"></slot>
</h1>
<p>
<slot></slot>
</p>
<div>
<slot name="footer"></slot>
</div>
</template>
</body>

如果你只需要小组件,你可以使用x-htmlx-data

<div x-data="{ message: '<p>Hello <strong>World!</strong></p>' }">
<span x-html="message"></span>
<span x-html="message"></span>
</div>

这将返回:

你好世界!
你好世界!

您可以在此处阅读文档

如前面的回答所述,Alpinejs 旨在为服务器端模板引擎添加一些前端反应性。

它可以使用指令来实现,但它有点复杂,我认为不值得付出努力。

下面我展示了一个我为处理清单而制作的一个小组件的示例,纯粹是用 Alpinejs 制作的(为了简洁起见,我删除了代码的某些部分,因此我们可以解决最相关的部分(:

Alpine.directive('checklist-dropdown', (el, { value, modifiers, expression }, { Alpine, effect, evaluate, evaluateLater, cleanup }) => {
var items = evaluate(items);
var tpl = `
<i x-show = 'checked_filters.length == 0' class = 'fa fa-filter'></i>
<i x-show = 'checked_filters.length > 0' class = 'fa fa-filter-circle-xmark' @click.stop  = 'clear()'></i>
<span class = 'text'>Checklist</span>
<span class = 'badge' x-show = 'checked_filters.length > 0' x-text = 'checked_filters.length'></span>
<template x-teleport = 'body'>
<div class = 'dropdown-panel' 
x-show = 'isOpen'
@click.outside = 'closePanel()'
x-transition.origin.top.left.duration.300ms
:style = '"top: " + (position.top + position.height) + "px; left: " + position.left + "px"'
>
<div class = 'dropdown-panel-scrollable' x-ref = 'scrollable'>
<template x-for = 'item in items'>
<div class = 'dropdown-panel-item' @click = 'toggleItem(item)'>
<input type = 'checkbox' :checked = 'isItemChecked(item)' />
<label x-text = 'itemDescription(item)'></label>
</div>
</template>
</div>

<div class = 'dropdown-panel-actions'>
<a class = 'button apply-button' @click = 'clickOnApplyButton()'>Aplicar</a>
</div>
</div>
</template>
`;
var data =  {
items: items,
isOpen: false,
position: el.getBoundingClientRect(),
_checklist: [], //modeled checklist
checklist: [], //checklist within the component (can be modified before confirming)
label: $(el).attr('label'),
init(){
Alpine.effect(() => {
this.checklist = this._checklist.slice();
});
},
isItemChecked(item){
return this.checklist.indexOf(item) != -1;
},
itemDescription(item){
//var desc = description_expr.replace(regex_matches[4], 'item');
var localScope = {};
localScope[item_name] = item;

return evaluate(description_expr, {scope: localScope});
},
toggleItem(item){
var pos = this.checklist.indexOf(item);

if ( pos == -1 ) this.checklist.push(item);
else this.checklist.splice(pos, 1);
},
togglePanel(){
if ( this.isOpen ) this.closePanel();
else this.openPanel();      
},
openPanel(){
this.position = el.getBoundingClientRect();
this.isOpen = true;
},
closePanel(){
this.isOpen = false;

setTimeout( () => {
$('.dropdown-panel-scrollable')[0].scrollTo(0, 0);
this.reset();
//x-ref not working inside template.
//this.$refs.scrollable.scrollTo(0, 0);
}, 250); //Wait until the transition is done.
},
apply(){
this._checklist = this.checklist.slice();
Alpine.nextTick( () => { //Needed to allow x-modelable to do its work
el.dispatchEvent(new CustomEvent('change', { detail: this._checklist }));
});
},
reset(){
this.checklist = this._checklist.slice();
},
clear(){
this._checklist = []; //If not present, causes isItemChecked to stop evaluating next time is open. Don´t know why.
this.checklist = [];
this.apply();
},
clickOnApplyButton(){
this.apply();
this.closePanel();
},
html: tpl
};
var reactiveData = Alpine.reactive(data);
var destroyScope = Alpine.addScopeToNode(el, reactiveData);
//reactiveData['init'] && evaluate(el, reactiveData['init'])
Alpine.bind(el, {'x-modelable': '_checklist'});
Alpine.bind(el, {'@click': 'togglePanel'});
Alpine.bind(el, {':class': '{active: checklist.length > 0}'});
Alpine.nextTick( () => {
Alpine.bind(el, {'x-html': 'html'});
});
evaluate(reactiveData['init']);

cleanup(() => {
destroyScope();
})
});

这就是我使用它的方式:

<div 
x-checklist-dropdown 
x-model = 'checked_filters'
items = '["option1", "option2"]'
>
</div>

您可能已经注意到,数据对象是您通常放置在 x-data 中的内容。

为了将这种反应性添加到任意 html 节点,我们使用以下两行:

var reactiveData = Alpine.reactive(data);
var destroyScope = Alpine.addScopeToNode(el, reactiveData);

接下来,我们需要绑定某些属性

Alpine.bind(el, {'x-modelable': '_checklist'});
Alpine.bind(el, {'@click': 'togglePanel'});
Alpine.bind(el, {':class': '{active: checklist.length > 0}'});

是最有趣的 x 可建模。通过将 x-modelable="_checklist" 添加到元素中,我们将它的值绑定到 x-model 的值,这就是x-model = 'checked_filters'的工作方式。

就是这样,我认为其余的台词几乎是自我描述的。

最新更新