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

新手求助:基于EJS下拉选框客户端过滤Mongoose数据失败

Hey there! Let's figure out why your selected category's products aren't showing up. Looking at your code, I spot a couple of key issues we can fix step by step.

First: Fix the dropdown option values

Right now, your <option> tags don't have a value attribute. That means when you select a category, you can't grab a unique identifier (like the category's ID) to filter its products. Let's update your EJS code to fix this:

<select id="catDropdown" class="ui fluid dropdown"> 
  <option value="">Category</option> 
  <% cats.forEach(cat => { %> 
  <option value="<%= cat._id %>"><%= cat.name %></option> 
  <% }); %> 
</select>

I switched to forEach for cleaner readability, and set the value to the category's MongoDB _id (it's guaranteed unique, which is way more reliable than using names if categories might have duplicates later).


Option 1: Filter products directly in the frontend (simple, since you already populated data)

Since you're using .populate("products") when fetching categories, every cat in your cats array already has its full list of associated products. We can use vanilla JavaScript to listen for dropdown changes and show the matching products instantly.

First, add a container in your EJS where you want products to appear:

<div id="productContainer" class="mt-4">
  <!-- Products will render here -->
</div>

Then add this script at the end of your EJS file (or in an external JS file):

// Grab DOM elements
const catDropdown = document.getElementById('catDropdown');
const productContainer = document.getElementById('productContainer');

// Pass the full categories array (with products) from EJS to JS
const allCategories = <%= JSON.stringify(cats) %>;

// Listen for dropdown selection changes
catDropdown.addEventListener('change', function() {
  const selectedCatId = this.value;
  
  // Clear previous products
  productContainer.innerHTML = '';
  
  if (!selectedCatId) {
    productContainer.innerHTML = '<p>Please select a category</p>';
    return;
  }
  
  // Find the selected category and its products
  const selectedCategory = allCategories.find(cat => cat._id === selectedCatId);
  const products = selectedCategory.products;
  
  // Handle empty product lists
  if (products.length === 0) {
    productContainer.innerHTML = '<p>No products in this category</p>';
    return;
  }
  
  // Generate HTML for each product
  let productHTML = '<ul class="product-list">';
  products.forEach(product => {
    productHTML += `
      <li>
        <h4>${product.name}</h4>
        <p>${product.description}</p>
        <span class="price">$${product.price.toFixed(2)}</span>
      </li>
    `;
  });
  productHTML += '</ul>';
  
  // Insert the finished HTML into the container
  productContainer.innerHTML = productHTML;
});

This works because we're leveraging the already-populated data you sent to the frontend, so no extra backend calls are needed.


Option 2: Use AJAX to fetch products from the backend (better for large datasets)

If you have tons of products, frontend filtering might slow things down. Instead, we can send an AJAX request to the backend when the dropdown changes, fetch only the selected category's products, then display them.

First, add a new route in your Node.js/Express code:

// Route to fetch products by category ID
app.get('/products/category/:id', (req, res) => {
  Category.findById(req.params.id)
    .populate('products')
    .exec((err, category) => {
      if (err) {
        console.log(err);
        return res.status(500).json({ error: 'Server error occurred' });
      }
      if (!category) {
        return res.status(404).json({ error: 'Category not found' });
      }
      res.json(category.products);
    });
});

Then update your frontend script (keep the same dropdown and product container from Option 1):

const catDropdown = document.getElementById('catDropdown');
const productContainer = document.getElementById('productContainer');

catDropdown.addEventListener('change', function() {
  const selectedCatId = this.value;
  
  productContainer.innerHTML = '';
  
  if (!selectedCatId) {
    productContainer.innerHTML = '<p>Please select a category</p>';
    return;
  }
  
  // Send AJAX GET request to our new backend route
  fetch(`/products/category/${selectedCatId}`)
    .then(response => {
      if (!response.ok) throw new Error('Failed to fetch products');
      return response.json();
    })
    .then(products => {
      if (products.length === 0) {
        productContainer.innerHTML = '<p>No products in this category</p>';
        return;
      }
      
      let productHTML = '<ul class="product-list">';
      products.forEach(product => {
        productHTML += `
          <li>
            <h4>${product.name}</h4>
            <p>${product.description}</p>
            <span class="price">$${product.price.toFixed(2)}</span>
          </li>
        `;
      });
      productHTML += '</ul>';
      
      productContainer.innerHTML = productHTML;
    })
    .catch(error => {
      console.error('Error:', error);
      productContainer.innerHTML = '<p>Failed to load products. Please try again.</p>';
    });
});

Quick Schema Consistency Tip

Just a small suggestion: your Product schema has a category field that's a String, but your Category schema references Product IDs. To keep your data relationships consistent, you might want to update the Product's category field to reference the Category ID instead:

const productSchema = new mongoose.Schema ({ 
  image: String, 
  name: {type: String, required: true}, 
  description: {type: String, required: true}, 
  category: {type: mongoose.Schema.Types.ObjectId, ref: "Category", required: true}, // Changed to ObjectId
  price: {type: Number, required: true} 
});

This way, you can easily populate the category from a product later if you need to.

内容的提问来源于stack exchange,提问作者Sam Van den Bosch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:37:51