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

如何在nopCommerce 4.20中用AJAX获取分类商品列表并实现筛选

Hey there! I’ve worked extensively with nopCommerce 4.20, so let’s walk through exactly how to build AJAX-powered category product listings and filters—no messy core code hacks required. We’ll break this into two main parts, with practical code snippets tailored to nopCommerce’s 4.20 API.

1. AJAX Category Product List Implementation

First, we’ll replace the default full-page reload when loading category products with an AJAX-driven partial refresh.

1.1 Create a Clean Action Method (Avoid Core Modifications)

Instead of editing the core ProductController, create a custom controller or add an action to a theme-specific controller (if using a custom theme). Here’s a sample action that returns a partial view of products:

public ActionResult LoadCategoryProducts(int categoryId, int page = 1, int pageSize = 12)
{
    // Use nopCommerce's built-in product service to fetch filtered products
    var products = _productService.SearchProducts(
        categoryIds: new List<int> { categoryId },
        storeId: _storeContext.CurrentStore.Id,
        visibleIndividuallyOnly: true,
        pageIndex: page - 1,
        pageSize: pageSize,
        languageId: _workContext.WorkingLanguage.Id
    );

    // Map products to the standard ProductOverviewModel using nopCommerce's mapper
    var productModels = _mappingEngine.Map<List<Product>, List<ProductOverviewModel>>(products);

    // Return the partial view with product data
    return PartialView("_ProductList", productModels);
}

1.2 Build a Reusable Product Partial View

Create Views/Product/_ProductList.cshtml to render product cards—match your site’s existing styling to keep consistency:

@model IList<ProductOverviewModel>

<div class="product-grid row">
    @foreach (var product in Model)
    {
        <div class="col-md-4 col-sm-6 mb-4">
            <div class="product-card">
                <a href="@Url.RouteUrl("Product", new { SeName = product.SeName })">
                    <img src="@product.DefaultPictureModel.ImageUrl" alt="@product.Name" class="img-fluid">
                </a>
                <h5 class="product-name"><a href="@Url.RouteUrl("Product", new { SeName = product.SeName })">@product.Name</a></h5>
                <div class="product-price">@product.Price</div>
                <button class="btn btn-primary add-to-cart" data-product-id="@product.Id">Add to Cart</button>
            </div>
        </div>
    }
</div>

1.3 Add AJAX Logic to the Category Page

Modify Views/Category/Category.cshtml to replace the default product list container with an AJAX-friendly wrapper, then add jQuery logic to load products dynamically:

<!-- Replace the existing product list with this container -->
<div id="ajax-product-container">
    @Html.Partial("_ProductList", Model.Products)
</div>

<script>
// Core AJAX function to load category products
function loadCategoryProducts(categoryId, page = 1) {
    $.ajax({
        url: '@Url.Action("LoadCategoryProducts", "Product")',
        type: 'GET',
        data: { categoryId: categoryId, page: page },
        beforeSend: function() {
            $('#ajax-product-container').addClass('loading'); // Add a loading state
        },
        success: function(response) {
            $('#ajax-product-container').html(response).removeClass('loading');
            // Update pagination links to use AJAX instead of full reload
            updatePaginationLinks(page);
        },
        error: function() {
            $('#ajax-product-container').html('<p class="text-danger">Failed to load products. Please try again.</p>');
        }
    });
}

// Bind category menu clicks to AJAX load (if needed)
$('.category-link').on('click', function(e) {
    e.preventDefault();
    const categoryId = $(this).data('category-id');
    loadCategoryProducts(categoryId);
});

// Helper to update pagination links
function updatePaginationLinks(currentPage) {
    $('.pagination a').on('click', function(e) {
        e.preventDefault();
        const page = $(this).data('page');
        const categoryId = '@Model.Category.Id';
        loadCategoryProducts(categoryId, page);
    });
}
</script>
2. AJAX Product Filter Implementation

Next, we’ll add dynamic filters (price range, brand, attributes) that update the product list without reloading the page.

2.1 Extend the Action Method to Accept Filter Params

Update the LoadCategoryProducts action to handle filter inputs:

public ActionResult LoadCategoryProducts(
    int categoryId, 
    int page = 1, 
    int pageSize = 12,
    decimal? minPrice = null,
    decimal? maxPrice = null,
    int? brandId = null,
    int[] attributeIds = null)
{
    var products = _productService.SearchProducts(
        categoryIds: new List<int> { categoryId },
        storeId: _storeContext.CurrentStore.Id,
        visibleIndividuallyOnly: true,
        priceMin: minPrice,
        priceMax: maxPrice,
        manufacturerId: brandId,
        productAttributeIds: attributeIds?.ToList(),
        pageIndex: page - 1,
        pageSize: pageSize,
        languageId: _workContext.WorkingLanguage.Id
    );

    var productModels = _mappingEngine.Map<List<Product>, List<ProductOverviewModel>>(products);
    return PartialView("_ProductList", productModels);
}

2.2 Create a Filters Partial View

Add Views/Product/_ProductFilters.cshtml to render filter controls—populate it with category-specific brands and attributes:

@model CategoryModel

<div id="ajax-filters" class="sidebar-filters">
    <!-- Price Range Filter -->
    <div class="filter-group">
        <h5>Price Range</h5>
        <input type="number" id="min-price" placeholder="Min" class="form-control mb-2">
        <input type="number" id="max-price" placeholder="Max" class="form-control mb-2">
        <button id="apply-price-filter" class="btn btn-sm btn-primary">Apply</button>
    </div>

    <!-- Brand Filter -->
    <div class="filter-group mt-4">
        <h5>Brand</h5>
        <select id="brand-filter" class="form-control">
            <option value="">All Brands</option>
            @foreach (var brand in Model.Brands)
            {
                <option value="@brand.Id">@brand.Name</option>
            }
        </select>
    </div>

    <!-- Attribute Filter -->
    <div class="filter-group mt-4">
        <h5>Attributes</h5>
        @foreach (var attribute in Model.ProductAttributes)
        {
            <div class="form-check">
                <input class="form-check-input attribute-filter" type="checkbox" value="@attribute.Id">
                <label class="form-check-label">@attribute.Name</label>
            </div>
        }
    </div>

    <button id="reset-filters" class="btn btn-sm btn-secondary mt-4">Reset Filters</button>
</div>

2.3 Add Filter AJAX Logic

Update the category page script to handle filter interactions:

<script>
// Collect current filter values
function getFilterParams() {
    const selectedAttributes = $('.attribute-filter:checked').map(function() {
        return $(this).val();
    }).get();

    return {
        minPrice: $('#min-price').val() || null,
        maxPrice: $('#max-price').val() || null,
        brandId: $('#brand-filter').val() || null,
        attributeIds: selectedAttributes.length > 0 ? selectedAttributes : null
    };
}

// Bind filter actions to AJAX load
$('#apply-price-filter').on('click', function() {
    const categoryId = '@Model.Category.Id';
    const filters = getFilterParams();
    loadCategoryProducts(categoryId, 1, filters);
});

$('#brand-filter').on('change', function() {
    const categoryId = '@Model.Category.Id';
    const filters = getFilterParams();
    loadCategoryProducts(categoryId, 1, filters);
});

$('.attribute-filter').on('change', function() {
    const categoryId = '@Model.Category.Id';
    const filters = getFilterParams();
    loadCategoryProducts(categoryId, 1, filters);
});

// Reset filters
$('#reset-filters').on('click', function() {
    $('#min-price').val('');
    $('#max-price').val('');
    $('#brand-filter').val('');
    $('.attribute-filter').prop('checked', false);
    const categoryId = '@Model.Category.Id';
    loadCategoryProducts(categoryId, 1);
});

// Update loadCategoryProducts to accept filters
function loadCategoryProducts(categoryId, page = 1, filters = {}) {
    $.ajax({
        url: '@Url.Action("LoadCategoryProducts", "Product")',
        type: 'GET',
        data: {
            categoryId: categoryId,
            page: page,
            minPrice: filters.minPrice,
            maxPrice: filters.maxPrice,
            brandId: filters.brandId,
            attributeIds: filters.attributeIds
        },
        beforeSend: function() {
            $('#ajax-product-container').addClass('loading');
        },
        success: function(response) {
            $('#ajax-product-container').html(response).removeClass('loading');
        },
        error: function() {
            $('#ajax-product-container').html('<p class="text-danger">Failed to load products. Please try again.</p>');
        }
    });
}
</script>
3. Critical Tips for nopCommerce 4.20
  • Avoid Core Edits: Use a custom theme or plugin to add these changes—never modify core nopCommerce files to maintain update compatibility.
  • Cache It: Add [OutputCache(Duration = 300, VaryByParam = "categoryId;page;minPrice;maxPrice;brandId;attributeIds")] to the AJAX action to boost performance.
  • User Experience: Add CSS for the loading class (e.g., a spinner overlay) to keep users informed during AJAX calls.
  • Visibility Checks: Always include visibleIndividuallyOnly: true in product searches to ensure only active, visible products are returned.

内容的提问来源于stack exchange,提问作者Dark Corner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:45:06