You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 01:35:16