【问题标题】:(VUE.js) The event doesn't work when called outside the component(VUE.js)在组件外部调用时事件不起作用
【发布时间】:2025-12-27 07:10:15
【问题描述】:

我正在为基于this tutorial 的产品制作一个小页面。在页面上,我有一个product 组件,我在其中保留了一个“添加到购物车”按钮。然而,卡片本身与组件分离并位于 index.html 内,这就是为什么我的 cart 属性被保留在 vue app 内(其中 app 是我在 index.html 中的根 div 的 id)。

问题:我需要我的“添加到购物车”按钮来增加购物车本身的数字。我真的不明白如何使用addToCartupdateCart 方法来做到这一点,如教程中所示。

谁能帮我解决这个问题?我将不胜感激任何帮助!提前致谢!

Vue.component('product', {
    props: {
      premium: {
        type: Boolean,
        required: true
      }
    },
    template: `
    <div id="product">
    
      <div class="product-image">
      <img :src="image" />      
      </div>
      
      <div class="product-info">
      
        <h1>{{ title }}</h1>
        <p>Shipping: {{ shipping }}</p>
        
        <p v-if="inStock">In Stock</p>
        <p v-else>Out of Stock</p>
        
        <h2>Details</h2>
        <ul>
          <li v-for="detail in details">{{ detail }}</li>
        </ul>

        <h3>Colors:</h3>
        <div v-for="(variant,index) in variants" :key="variant.variantId">
          <div class="color-box" :style="{ backgroundColor: variant.variantColor }" @mouseover="updateProduct(index)"></div>
        </div>

        <button :class="{ disabledButton: !inStock }" v-on:click="add-to-cart" :disabled="!inStock">Add to Cart</button>
      </div>

    </div>
    `,
    data() {
      return {
        product: "Socks",
        brand: "Vue Mastery",
        selectedVariant: 0,
        details: ["80% cotton", "20% polyester", "Gender-neutral"],
        variants: [
            {
              variantId: 2234,
              variantQuantity: 15,
              variantColor: "green",
              variantImage: "./assets/vmSocks-green.jpg"     
            },
            {
              variantId: 2235,
              variantQuantity: 0,
              variantColor: "blue",
              variantImage: "./assets/vmSocks-blue.jpg"
            }
        ]
      }
    },
    methods: {
      addToCart() {
        this.$emit('add-to-cart')
      },
      updateProduct(index) {
        this.selectedVariant = index
      }
    },
    computed: {
      title() {
        return this.brand + ' ' + this.product
      },
      image() {
        return this.variants[this.selectedVariant].variantImage
      },
      inStock() {
        if (this.variants[this.selectedVariant].variantQuantity > 0) {
          return true
        } else {
          return false
        }
      },
      shipping() {
        if (this.premium) {
          return "Free"
        } else {
          return 2.99
        }
      }
    }
  })
  
  var app = new Vue({
    el: '#app',
    data: {
      premium: true,
      cart: 0
    },
    methods: {
        updateCart() {
          this.cart += 1
        }
    }
  })
body {
  font-family: tahoma;
  color:#282828;
  margin: 0px;
}

.nav-bar {
  background: linear-gradient(-90deg, #84CF6A, #16C0B0);
  height: 60px;
  margin-bottom: 15px;
}

.product {
  display: flex;
  flex-flow: wrap;
  padding: 1rem;
}

img {
  border: 1px solid #d8d8d8;
  width: 70%;
  margin: 40px;
  box-shadow: 0px .5px 1px #d8d8d8;
}

.product-image {
  width: 80%;
}

.product-image,
.product-info {
  margin-top: 10px;
  width: 50%;
}

.color-box {
  width: 40px;
  height: 40px;
  margin-top: 5px;
}

.cart {
  margin-right: 25px;
  float: right;
  border: 1px solid #d8d8d8;
  padding: 5px 20px;
}

button {
  margin-top: 30px;
  border: none;
  background-color: #1E95EA;
  color: white;
  height: 40px;
  width: 100px;
  font-size: 14px;
} 

.disabledButton {
  background-color: #d8d8d8;
}

.review-form {
  width: 400px;
  padding: 20px;
  margin: 40px;
  border: 1px solid #d8d8d8;
}

input {
  width: 100%;  
  height: 25px;
  margin-bottom: 20px;
}

textarea {
  width: 100%;
  height: 60px;
}

.tab {
  margin-left: 20px;
  cursor: pointer;
}

.activeTab {
  color: #16C0B0;
  text-decoration: underline;
}
<!DOCTYPE html>
<html>
    <head>
        <meta name="viewpoint" content="width=devide-width, initial-scale=1">
        <link rel="stylesheet" href="style.css">
        <title>Vue app</title>
    </head>
    <body>
        <div class="nav-bar"></div>

        <div id="app">
            <div class="cart">
                <p>Cart({{ cart }})</p>
            </div>
            
            <product :premium="premium" @add-to-cart="updateCart"></product>    
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="prior.js"></script>
    </body> 
</html>

【问题讨论】:

    标签: javascript html css vue.js


    【解决方案1】:

    改变这个:

     <button :class="{ disabledButton: !inStock }" v-on:click="add-to-cart" :disabled="!inStock">Add to Cart</button>
    

    变成这样:

     <button :class="{ disabledButton: !inStock }" v-on:click="addToCart" :disabled="!inStock">Add to Cart</button>
    

    所以addToCart: 这个函数会发出add-to-cart 事件

    你也可以立即运行 emmit onClick 而不添加函数:

     <button :class="{ disabledButton: !inStock }" v-on:click="$emit('add-to-cart')" :disabled="!inStock">Add to Cart</button>
    

    Vue.component('product', {
        props: {
          premium: {
            type: Boolean,
            required: true
          }
        },
        template: `
        <div id="product">
        
          <div class="product-image">
          <img :src="image" />      
          </div>
          
          <div class="product-info">
          
            <h1>{{ title }}</h1>
            <p>Shipping: {{ shipping }}</p>
            
            <p v-if="inStock">In Stock</p>
            <p v-else>Out of Stock</p>
            
            <h2>Details</h2>
            <ul>
              <li v-for="detail in details">{{ detail }}</li>
            </ul>
    
            <h3>Colors:</h3>
            <div v-for="(variant,index) in variants" :key="variant.variantId">
              <div class="color-box" :style="{ backgroundColor: variant.variantColor }" @mouseover="updateProduct(index)"></div>
            </div>
    
            <button :class="{ disabledButton: !inStock }" v-on:click="addToCart" :disabled="!inStock">Add to Cart</button>
          </div>
    
        </div>
        `,
        data() {
          return {
            product: "Socks",
            brand: "Vue Mastery",
            selectedVariant: 0,
            details: ["80% cotton", "20% polyester", "Gender-neutral"],
            variants: [
                {
                  variantId: 2234,
                  variantQuantity: 15,
                  variantColor: "green",
                  variantImage: "./assets/vmSocks-green.jpg"     
                },
                {
                  variantId: 2235,
                  variantQuantity: 0,
                  variantColor: "blue",
                  variantImage: "./assets/vmSocks-blue.jpg"
                }
            ]
          }
        },
        methods: {
          addToCart() {
            this.$emit('add-to-cart')
          },
          updateProduct(index) {
            this.selectedVariant = index
          }
        },
        computed: {
          title() {
            return this.brand + ' ' + this.product
          },
          image() {
            return this.variants[this.selectedVariant].variantImage
          },
          inStock() {
            if (this.variants[this.selectedVariant].variantQuantity > 0) {
              return true
            } else {
              return false
            }
          },
          shipping() {
            if (this.premium) {
              return "Free"
            } else {
              return 2.99
            }
          }
        }
      })
      
      var app = new Vue({
        el: '#app',
        data: {
          premium: true,
          cart: 0
        },
        methods: {
            updateCart() {
              this.cart += 1
            }
        }
      })
    body {
      font-family: tahoma;
      color:#282828;
      margin: 0px;
    }
    
    .nav-bar {
      background: linear-gradient(-90deg, #84CF6A, #16C0B0);
      height: 60px;
      margin-bottom: 15px;
    }
    
    .product {
      display: flex;
      flex-flow: wrap;
      padding: 1rem;
    }
    
    img {
      border: 1px solid #d8d8d8;
      width: 70%;
      margin: 40px;
      box-shadow: 0px .5px 1px #d8d8d8;
    }
    
    .product-image {
      width: 80%;
    }
    
    .product-image,
    .product-info {
      margin-top: 10px;
      width: 50%;
    }
    
    .color-box {
      width: 40px;
      height: 40px;
      margin-top: 5px;
    }
    
    .cart {
      margin-right: 25px;
      float: right;
      border: 1px solid #d8d8d8;
      padding: 5px 20px;
    }
    
    button {
      margin-top: 30px;
      border: none;
      background-color: #1E95EA;
      color: white;
      height: 40px;
      width: 100px;
      font-size: 14px;
    } 
    
    .disabledButton {
      background-color: #d8d8d8;
    }
    
    .review-form {
      width: 400px;
      padding: 20px;
      margin: 40px;
      border: 1px solid #d8d8d8;
    }
    
    input {
      width: 100%;  
      height: 25px;
      margin-bottom: 20px;
    }
    
    textarea {
      width: 100%;
      height: 60px;
    }
    
    .tab {
      margin-left: 20px;
      cursor: pointer;
    }
    
    .activeTab {
      color: #16C0B0;
      text-decoration: underline;
    }
    <!DOCTYPE html>
    <html>
        <head>
            <meta name="viewpoint" content="width=devide-width, initial-scale=1">
            <link rel="stylesheet" href="style.css">
            <title>Vue app</title>
        </head>
        <body>
            <div class="nav-bar"></div>
    
            <div id="app">
                <div class="cart">
                    <p>Cart({{ cart }})</p>
                </div>
                
                <product :premium="premium" @add-to-cart="updateCart"></product>    
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <script src="prior.js"></script>
        </body> 
    </html>

    【讨论】: