EJS forEach / for 循环未定义问题

EJS forEach / for loop undefined issue

我定义了以下路线

    app.get("/product/:category", (req, res, next) => {
    const category = req.params.category
    let selectedProducts = []
    products.forEach(product => {
        if(product.category === category)
        selectedProducts.push(product)
    })
    res.render("category_view", {products: selectedProducts, customizationItems: customizationItems, category: category, header: category, pageType: ""});
});

它正在将一组对象发送到定义为“类别视图”的 ejs 文件。以下是

<%- include ("partials/header.ejs") %>
<div class="category-enclosure">
<div class="container">
  <div class="row">
    <div class="col s12">
        <div class="category-list">
          <!-- <div class="category-bar hideme" >
            <h1><%-category%></h1>
        </div> -->
        <div class="searchbar" >
          <input type="text" placeholder="Search For Product... ">
          <i class="material-icons">search</i>  
        </div>
    
            <% products.forEach(item => { %>
              <div class="floating-product-img disabled" id="<%- item.id %>">
                <img src="<%- item.img %>">
              </div>
              <div class="floating-customization-menu disabled" id="<%- item.id %>">
                <div class="customize-header">
                  <h1>Chocolate Chip</h1>
                  <h2>.33</h2>
                </div>
                <div class="customize-body">
                  <form action="#">
                      <ul class="collection">
                        <% for(let i = 0; i < customizationItems.length; i++) { %>
                          <% if (item.toppings[i].title === customizationItems[i]) { %>
                            <li class="collection-item"> 
                              <div>
                                <p>
                                <label>
                                  <input type="checkbox" class="filled-in" checked="checked" />
                                  <span><%-item.toppings[i].title %></span>
                                </label>
                              </p>
                            </div>  
                            <div>
                              <a href=""><i class="material-icons">remove_circle_outline</i></a>
                              <p></p>
                              <a href=""><i class="material-icons">add_circle_outline</i></a>
                            </div>
                            <div>
                              <h2><%-item.toppings[i].amount %></h2>
                            </div>
                          </li>
                            
                         <% } else { %>
                          <li class="collection-item"> 
                            <div>
                              <p>
                              <label>
                                <input type="checkbox" />
                                <span><%-customizationItems[i] %></span>
                              </label>
                            </p>
                          </div>  
                          <div>
                            <a href=""><i class="material-icons">remove_circle_outline</i></a>
                            <p></p>
                            <a href=""><i class="material-icons">add_circle_outline</i></a>
                          </div>
                          <div>
                            <h2></h2>
                          </div>
                        </li>
                        
                         
                         <% }%>
                      <% } %>
                      </ul>
                  </form>
                </div>
                <div class="customize-footer">
                  <div>
                    <h2>Total</h2>
                    <h2>.00</h2>
                  </div>
                  <div class="customize-footer-btns">
                      <a class="waves-effect waves-light btn cancle-btn">Cancle</a>
                      <a class="waves-effect waves-light btn">Add to Cart</a>
                  </div>
                </div>
              </div>
              <div class="card category-card">
                <div class="card-image waves-effect waves-block waves-light product-img">
                  <img src="<%-item.img%>">
                  <i class="image-expand-button fas fa-expand-alt" id="<%- item.id %>"></i>
                </div>
                <div class="card-content category-card-content">
                    <div class="category-card-title">
                      <span class="card-title activator grey-text text-darken-4"><%- item.name %></span>
                      <p>$<%- item.price %></p>
                  </div>
                  <div class="card-btns">
                    <div>
                      <i class="fas fa-angle-up activator"></i>
                    </div>
                    <div>
                      <a href=""><button>Add to Cart</button></a>
                    </div>
                   
                  </div>
                </div>
                <div class="card-reveal">
                  <span class="card-title grey-text text-darken-4"><%- item.name %><i class="material-icons right">close</i></span>
                  <p>Here is some more information about this product that is only revealed once clicked on.</p>
                  <a class="waves-effect waves-light btn-small">Add to Cart</a>
                  <a class="waves-effect waves-light btn-small customize-btn" id="<%- item.id %>">Customize</a>

                </div>
              </div>
            <%}) %>
          </div>
    </div>
  </div>
</div>
<%- include ("partials/footer.ejs") %>

这是对象数组:

const products = [
{
    id: "1",
    name: "chocolate chip",
    category: "cookies",
    description: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever",
    img: "https://images.unsplash.com/photo-1486428128344-5413e434ad35?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
    price: 6.33,
    toppings: [{name: "cherries", amount: 2} , {name: "nuts", amount: 1}, {name: "chocolate flakes", amount: 1}]

},
{
    id: "2",
    name: "peanut butter",
    category: "cookies",
    description: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever",
    img: "https://images.unsplash.com/photo-1519869491916-8ca6f615d6bd?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
    price: 4.77,
    toppings: [{title: "cherries", amount: 1} , {title: "nuts", amount: 1}, {title: "chocolate flakes", amount: 1}]
},
{
    id: "3",
    name: "chocolate chip",
    category: "cookies",
    description: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever",
    img: "https://images.unsplash.com/photo-1486427944299-d1955d23e34d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80",
    price: 6.33,
    toppings: [{title: "cherries", amount: 1} , {title: "nuts", amount: 1}, {title: "chocolate flakes", amount: 1}]

},

];

这是第二个数组:

const customizationItems =  ["cholate drizzle", "chocolate flakes", "strawberries", "cherries", "nuts"]

在这个 ejs 文件中,我使用 forEach 语句遍历一个名为 products 的对象。这会为数组中的每个对象呈现一个元素。在此循环内,我还有一个“for 循环”,用于将 属性“customizationItems”下的数组与另一个数组进行比较,该数组是包含 forEach 语句的项目的 属性,这样引用“item.toppings[i].name”。但是,此 属性 在页面呈现时未优化。

我已经验证了这个 属性 不等于 undefined 只要“I”不超过“toppings array”的长度。通常,如果我与字符串数组进行比较,则没有问题,并且由于未定义的值而触发 else 语句,因为这两个数组的长度不同。

我想知道当“item.toppings[i].name”的值等于未定义时,为什么没有触发 else 语句呈现替代元素。

这是浏览器错误:

TypeError: /home/devindavis/Documents/webdev/bakery-project/views/category_view.ejs:28
    26|                       <ul class="collection">
    27|                         <% for(let i = 0; i < customizationItems.length; i++) { %>
 >> 28|                           <% if ( item.toppings[i].title === customizationItems[i]) { %>
    29|                             <li class="collection-item"> 
    30|                               <div>
    31|                                 <p>

Cannot read property 'name' of undefined
    at products.forEach.item (/home/devindavis/Documents/webdev/bakery-project/views/category_view.ejs:31:30)
    at Array.forEach (<anonymous>)
    at eval (/home/devindavis/Documents/webdev/bakery-project/views/category_view.ejs:16:17)
    at category_view (/home/devindavis/Documents/webdev/bakery-project/node_modules/ejs/lib/ejs.js:691:17)
    at tryHandleCache (/home/devindavis/Documents/webdev/bakery-project/node_modules/ejs/lib/ejs.js:272:36)
    at View.exports.renderFile [as engine] (/home/devindavis/Documents/webdev/bakery-project/node_modules/ejs/lib/ejs.js:489:10)
    at View.render (/home/devindavis/Documents/webdev/bakery-project/node_modules/express/lib/view.js:135:8)
    at tryRender (/home/devindavis/Documents/webdev/bakery-project/node_modules/express/lib/application.js:640:10)
    at Function.render (/home/devindavis/Documents/webdev/bakery-project/node_modules/express/lib/application.js:592:3)
    at ServerResponse.render (/home/devindavis/Documents/webdev/bakery-project/node_modules/express/lib/response.js:1012:7)

查看ejs.render返回的错误:

    27|                         <% for(let i = 0; i < customizationItems.length; i++) { %>
 >> 28|                           <% if ( item.toppings[i].title === customizationItems[i]) { %>

正如您在问题中指出的那样,您正在使用 i 遍历 customizationItems 数组。但是随后您使用相同的索引来查看名为 toppings 的不同数组的元素。因此,尝试取消引用 item.toppings[i].title 可能会引发异常 - 超出 Javascript 中数组的范围不会引发异常,但取消引用未定义的值会。

进一步分解:

假设 item.toppings 是一个有 2 个元素的数组,而 customizationItems 有 4 个。 当 i = 2item.toppings[i] 试图从包含两项的数组中获取第三项时。这叫做越界,returns在javascript.

中未定义

所以在这个例子中,表达式 item.toppings[i].title when item.toppings[i] is undefined 正如我们刚才讨论的那样,试图从未定义的抛出中获取 title 属性 的值一个例外。

相反,您应该将数组迭代与循环内引用的元素相匹配。例如,查找与自定义项相同的置顶标题,我们可以像这样循环遍历两个数组:

for(let i = 0; i < customizationItems.length; i++) {
    for(let n = 0; n < item.toppings.length; n++) {
        if ( item.toppings[n].title === customizationItems[i]) {
            // do stuff

这也可以这样写,完全消除了数字索引变量:

for(let customizationItem of customizationItems) {
    for(let topping of item.toppings) {
        if ( topping.title === customizationItem ) {
            // do stuff

这样可以更容易地了解我们如何遍历两个数组并比较它们的内容。