Alpine.js报错:total未定义,WordPress分类加载文章异常
解决Alpine.js "total is not defined" 错误
问题概述
开发了一个展示WordPress文章、支持分类筛选加载的程序,目前控制台持续报错:
Alpine Expression Error: total is not defined
对应触发表达式为"total > (limit + offset)",以下是当前完整代码:
<?php /* Template Name: test */ ?> <html> <head> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <script> document.addEventListener('alpine:init', () => { Alpine.data("filterPosts", (adminURL) => ({ posts: null, category: null, showDefault: true, showFiltered: false, loadingNew: false, // <--- defaults to false loadingMore: false, // <--- defaults to false offset: 0, filterPosts(id) { this.loadingNew = true; this.showDefault = false; this.showFiltered = true; this.category = id; this.offset = 0; // <-- reset offset to zero this.fetchPosts(); }, loadMore() { this.loadingMore = true; this.offset += 10; this.showFiltered = true; this.fetchPosts(true); }, fetchPosts(append = false) { var formData = new FormData(); formData.append("action", "filterPosts"); formData.append("offset", this.offset); // <-- Add new data to the form // formData.append("limit", this.limit); if (this.category) { formData.append("category", this.category); } fetch(adminURL, { method: "POST", body: formData, }) .then((res) => res.json()) .then((res) => { if (append) { // Appends posts to the end of existing posts data this.posts = this.posts.concat(res.posts); } else { // Resets the posts data with new data this.posts = res.posts; } this.loading = false; // Reset both loading states // this.loadingNew = false; // this.loadingMore = false; }); }, getTotal() { var formData = new FormData(); formData.append("action", "filterPosts"); fetch(adminURL, { method: "POST", body: formData, }) .then((res) => res.json()) .then((res) => { this.total = res.total; }); }, init() { this.getTotal(); } })); }) </script> </head> <body> <div x-data="filterPosts('<?php echo admin_url('admin-ajax.php'); ?>')"> <section <?php theme_section_attr_id() ?> <?php theme_section_attr_class('main-section js-posts') ?>> <div class="container"> <div class="heading mx-0"> <?php $before_title = get_sub_field('before_title'); if ($before_title) : ?> <strong class="sub-title"><?php echo $before_title ?></strong> <?php endif ?> <?php $title = get_sub_field('title'); if ($title) : ?> <h2><?php echo $title ?></h2> <?php endif ?> </div> <!--<div class="head js-posts-search-text">--> <!-- <?php if ($search_value) : ?>--> <!-- <h2 class="h5 fw-semibold"><?php printf(__('Showing results for “%s”', 'base'), $search_value) ?></h2>--> <!-- <?php endif ?>--> <!--</div>--> <!--alipne js dynamic post start--> <div class="has-filter row flex-md-row-reverse"> <div class="col-md-8 col-lg-9 js-posts-holder"> <!-- Posts Column --> <div x-show.important="showDefault" class="row cards-area js-posts-items"> <!-- Default posts query --> <?php get_template_part( 'template-parts/posts-filter/default-query' ); ?> <!-- Load More Posts --> <div class="text-center"> <button @click="loadMore()" x-show="total > (limit + offset)" class="bg-white border border-solid border-slate-200 hover:bg-slate-100 px-4 py-2"> Load More</button> </div> </div> <!-- Filtered posts --> <div x-show.important="showFiltered" x-html="posts" class="row cards-area js-posts-items"> <!-- Default posts query --> </div> <!-- Skeleton Loader --> <template x-if="loadingNew || loadingMore"> <div class="grid grid-cols-2 gap-6"> <template x-for="i in 4"> <div> <div class="h-52 w-full bg-slate-200 rounded-lg"></div> <div class="h-8 w-1/2 bg-slate-200 rounded-lg mt-4"></div> </div> </template> </div> </template> </div> <!-- Filtered posts --> <div class="col-md-4 col-lg-3"> <aside class="sidebar"> <div class="widget widget-filter"> <button class="widget-title">Explore Topics</button> <svg class="separator" viewBox="0 0 238 11" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"> <line opacity="0.3" y1="0.5" x2="101.942" y2="0.5" stroke="#3CD5AF"></line> <line opacity="0.3" y1="10.5" x2="237.5" y2="10.5" stroke="#3CD5AF"></line> </svg> <?php $categories = get_categories(); if ( ! empty( $categories ) ) : ?> <ul class="filter-list"> <?php foreach ( $categories as $category ) : // Skip "Uncategorized" category if ($category->name == 'Uncategorized') continue; ?> <li :class=" category == <?php echo $category->term_id; ?> ? '' : ''"> <a hreff="" @click="filterPosts(<?= $category->term_id; ?>)"><?= esc_html( $category->name ); ?></button> </li> <?php endforeach; ?> </ul> <?php endif; ?> </div> </aside> <!-- Filtered end --> </div> </div> </div> <!--alipne js dynamic post end--> </section> </div> </body> </html>
错误原因
- 未初始化核心变量:Alpine.data的初始属性中未声明
total和limit,DOM渲染时无法找到这两个变量。 - 异步加载时序问题:
getTotal()是异步请求,页面初始渲染时total还未被赋值,触发未定义错误。 - 代码逻辑遗漏:
fetchPosts中使用了未定义的this.loading,应该改为控制loadingNew和loadingMore。- 分类链接存在标签不匹配(
<a>对应</button>)和hreff拼写错误。
修复方案
1. 初始化缺失的变量
在Alpine.data的初始属性中添加total和limit:
Alpine.data("filterPosts", (adminURL) => ({ posts: null, category: null, showDefault: true, showFiltered: false, loadingNew: false, loadingMore: false, offset: 0, total: 0, // 初始化total为0 limit: 10, // 初始化分页数量,根据需求调整 // ... 其他方法 }));
2. 修复按钮显示的表达式
添加空值判断,避免初始渲染时的错误:
<button @click="loadMore()" x-show="total !== 0 && total > (limit + offset)" class="bg-white border border-solid border-slate-200 hover:bg-slate-100 px-4 py-2"> Load More </button>
3. 修复fetchPosts中的loading状态
取消注释并修正loading状态的控制:
.then((res) => { if (append) { this.posts = this.posts.concat(res.posts); } else { this.posts = res.posts; } // 修复loading状态 this.loadingNew = false; this.loadingMore = false; });
4. 修复分类链接的语法错误
修正标签不匹配和拼写问题,添加@click.prevent阻止默认跳转:
<li :class="category == <?php echo $category->term_id; ?> ? 'active' : ''"> <a href="#" @click.prevent="filterPosts(<?= $category->term_id; ?>)"><?= esc_html( $category->name ); ?></a> </li>
5. 启用limit参数(可选)
如果需要传递limit到后端,取消fetchPosts中对应代码的注释:
formData.append("limit", this.limit);
内容的提问来源于stack exchange,提问作者Beep
相关产品推荐
相关产品推荐

