如何在AlpineJS中将换行符 转为<li>标签及相关功能实现
Alpine.js 解决方案:换行转列表、分页加载、多条件筛选
1. 解决换行符转<li>标签显示问题
x-text会自动转义HTML标签,必须改用x-html渲染处理后的HTML字符串。写一个工具函数将\n分割的文本转为<ul>包裹的<li>列表:
function formatTerms(terms) { if (!terms) return ''; // 分割换行符并过滤空行 const lines = terms.split('\n').filter(line => line.trim() !== ''); return `<ul>${lines.map(line => `<li>${line.trim()}</li>`).join('')}</ul>`; }
模板中使用方式:
<template x-for="item in filteredItems" :key="item.id"> <div class="item"> <h3 x-text="item.title"></h3> <div x-html="formatTerms(item.terms)"></div> </div> </template>
2. 实现加载更多/分页功能
通过维护当前页码、每页条数、总页数,点击按钮时请求下一页数据并合并到现有列表:
- 在
x-data中定义分页变量:page: 1,perPage: 5,totalPages: 1,items: [],loading: false - 编写
fetchItems函数,根据页码请求API - 加载更多按钮判断是否还有下一页,点击后触发请求
3. 即时输入筛选+下拉选择筛选
定义筛选条件变量searchKeyword和selectedCategory,通过计算逻辑实时过滤列表:
- 输入框绑定
x-model="searchKeyword" - 下拉框绑定
x-model="selectedCategory" - 过滤逻辑:匹配关键词(不区分大小写)和选中分类
完整示例代码
<div x-data="{ items: [], filteredItems: [], page: 1, perPage: 5, totalPages: 1, loading: false, searchKeyword: '', selectedCategory: 'all', // 模拟API请求,替换为你的真实接口地址 async fetchItems(page) { this.loading = true; const res = await fetch(`https://api.example.com/items?page=${page}&per_page=${this.perPage}`); const data = await res.json(); // 合并新数据,第一页直接覆盖,后续页追加 this.items = page === 1 ? data.items : [...this.items, ...data.items]; this.totalPages = data.total_pages; this.loading = false; this.updateFilteredItems(); }, loadMore() { if (this.page < this.totalPages && !this.loading) { this.page++; this.fetchItems(this.page); } }, updateFilteredItems() { let result = [...this.items]; // 关键词筛选 if (this.searchKeyword.trim()) { const keyword = this.searchKeyword.toLowerCase(); result = result.filter(item => item.title.toLowerCase().includes(keyword) || item.terms.toLowerCase().includes(keyword) ); } // 分类筛选 if (this.selectedCategory !== 'all') { result = result.filter(item => item.category === this.selectedCategory); } this.filteredItems = result; }, formatTerms(terms) { if (!terms) return ''; const lines = terms.split('\n').filter(line => line.trim() !== ''); return `<ul>${lines.map(line => `<li>${line.trim()}</li>`).join('')}</ul>`; } }" x-init="fetchItems(1)" x-effect="updateFilteredItems()"> <!-- 筛选区域 --> <div class="filters"> <input type="text" placeholder="搜索标题或条款..." x-model="searchKeyword" class="search-input" > <select x-model="selectedCategory" class="category-select"> <option value="all">所有分类</option> <option value="tech">技术</option> <option value="life">生活</option> <option value="work">工作</option> </select> </div> <!-- 列表展示 --> <div class="items-list"> <template x-for="item in filteredItems" :key="item.id"> <div class="item-card"> <h4 x-text="item.title"></h4> <div class="terms" x-html="formatTerms(item.terms)"></div> <span class="category" x-text="item.category"></span> </div> </template> </div> <!-- 加载更多按钮 --> <div class="load-more-container"> <button x-show="page < totalPages" x-on:click="loadMore()" x-bind:disabled="loading" > <span x-show="!loading">加载更多</span> <span x-show="loading">加载中...</span> </button> <p x-show="page >= totalPages && items.length > 0">已加载全部内容</p> <p x-show="items.length === 0 && !loading">暂无数据</p> </div> </div> <style> /* 基础样式,可按需调整 */ .filters { margin: 20px 0; display: flex; gap: 10px; } .search-input, .category-select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; } .items-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; margin-bottom: 20px; } .item-card { border: 1px solid #eee; padding: 15px; border-radius: 8px; } .terms ul { margin: 10px 0; padding-left: 20px; } .load-more-container { text-align: center; } button { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:disabled { background: #ccc; cursor: not-allowed; } </style>
关键说明
- 换行转列表:
formatTerms处理文本后通过x-html渲染,注意确保terms内容安全(若包含用户输入需做XSS转义) - 分页加载:通过
page变量控制请求页码,合并新数据避免覆盖原有内容 - 多条件筛选:
updateFilteredItems整合关键词和分类筛选逻辑,x-effect监听条件变化自动更新结果 - 加载状态:
loading变量控制按钮禁用状态和加载提示
内容的提问来源于stack exchange,提问作者user2707393
相关产品推荐
相关产品推荐

