vue.js - vuejs中无法通过eventBus检索数据
问题描述
我的 vuejs 中有一个部分,我试图product-tabs
通过eventBus
. 似乎评论只是拒绝显示。谁能帮助我理解为什么通过 eventBus 发送数据不起作用?
main.js
var eventBus = new Vue()
Vue.component('product-tabs', {
props: {
reviews: {
type: Array,
required: true
}
},
template: `
<div>
<span class="tab"
:class="{ activeTab: selectedTab === tab}"
v-for="(tab, index) in tabs" :key="index"
@click="selectedTab = tab">
{{tab}}</span>
<div v-show="selectedTab === 'Reviews'">
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul v-else>
<li v-for="(review, index) in reviews" :key="index">
<p>{{ review.name }}</p>
<p>Rating:{{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<product-review v-show="selectedTab === 'Make a review'"></product-review>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a review'],
selectedTab: 'Reviews'
}
}
})
Vue.component('product-review', {
template: `
<!-- submit the form only if it is filled -->
<form class="review-form" @submit.prevent="onSubmit">
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<label for="name">Name:</label>
<input id="name" v-model="name" placeholder="name">
</p>
<p>
<label for="review">Review:</label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<!-- .number is a modifier that makes sure typecast is a number -->
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<p>
<fieldset>
<legend>Would you recommend this product?</legend>
<input class="" type="radio" name="recommend" v-model="recommend" value="yes">Yes
<input class="" type="radio" name="recommend" v-model="recommend" value="no">No
</fieldset>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
`,
data() {
return {
name: null,
review: null,
rating: null,
recommend: null,
errors: []
}
},
methods: {
onSubmit() {
if(this.name && this.review && this.rating && this.recommend) {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating,
recommend: this.recommend,
error: []
}
eventBus.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = null
this.recommend = null
} else {
if(!this.name) this.errors.push("Name required.")
if(!this.review) this.errors.push("Review required.")
if(!this.rating) this.errors.push("Rating required.")
if(!this.recommend) this.errors.push("Recommendation required.")
}
}
}
})
Vue.component('product', {
props: {
premium: {
required: true,
type: Boolean
}
},
template: `
<div class="product">
<div id="product-image">
<!-- bind the value of image in data object to src in our img tag-->
<img class="product-image" v-bind:src="image" v-bind:alt="altText">
<a v-bind:href="vbindDoc"></a>
</div>
<div id="product-info">
<span class="allmyText">
<h2>{{ title }}</h2>
<p v-show="inStock">In stock</p>
<span v-show='onSale'>
<p>{{ isOnSale }}</p>
</span>
<p>Shipping charges: {{ isShipping }}</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div v-for="(variant, index) in variants"
:key="variant.variantId"
class="color-box"
:style="{ backgroundColor: variant.variantColor }"
@mouseover="updateProduct(index)">
</div>
<ul>
<li v-for="size in sizes">{{ size }}</li>
</ul>
</span>
</div>
<button class="cart" v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>Add to cart</button>
<button class="cart" v-on:click="removeFromCart"
:class="{ disabledButton: !inStock }"
>Remove from cart</button>
<!-- when there is stock, add a text-decoration: line-through;
This is done by binding a class -->
<p :class="{ outofStock: inStock}">Out of stock</p>
<product-tabs :reviews="reviews"></product-tabs>
</div>
`,
data() {
return {
product: "Socks",
brand: "Alan's branded",
description: "A pair of warm and fuzzy",
selectedVariant: 0,
altText: "A pair of socks",
vbindDoc: "https://www.vuemastery.com/courses/intro-to-vue-js/attribute-binding",
onSale: true,
details: ["80% Cotton", "20% polyesterer", "Gender-neutral"],
variants: [
{
variantId: 2232,
variantColor: "green",
variantImage: "https://www.vuemastery.com/images/challenges/vmSocks-green-onWhite.jpg",
variantQuantity: 10
},
{
variantId: 2233,
variantColor: "blue",
variantImage: "https://www.vuemastery.com/images/challenges/vmSocks-blue-onWhite.jpg",
variantQuantity: 5
}
],
sizes: [
"small",
"medium",
"large"
],
reviews: []
}
},
methods: {
addToCart() {
this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId)
},
removeFromCart() {
this.$emit('remove-from-cart', this.variants[this.selectedVariant].variantId)
},
updateProduct(index) {
this.selectedVariant = index
console.log(index)
}
},
computed: {
title() {
return this.brand + " " + this.product
},
image() {
return this.variants[this.selectedVariant].variantImage
},
inStock() {
return this.variants[this.selectedVariant].variantQuantity
},
isOnSale() {
return this.brand + " " + this.product + " is on sale"
},
isShipping() {
if (this.premium) {
return "Free"
} else {
return 8.88
}
},
mounted() {
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
}
}
});
var app = new Vue({
el: '#app',
data: {
premium: false,
cart: []
},
methods: {
updateCart(id) {
this.cart.push(id)
},
removeFromCart(id) {
if (this.cart.length >= 1) {
this.cart.shift(id)
}
}
}
});
索引.html
<html lang="en">
<head>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="app">
<product :premium="premium" @add-to-cart="updateCart" @remove-from-cart="removeFromCart"></product>
<div class="cart">
<p>Cart({{cart.length}})</p>
</div>
</div>
<!-- Take it from this url https://vuejs.org/v2/guide/installation.html -->
<script src="https://unpkg.com/vue"></script>
<script src='./main.js'></script>
</body>
</html>
样式.css
#app {
padding: 0;
margin: 0;
width: 1024px;
height: 600px;
/* border: 1px solid black; */
}
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;
height: 480px;
}
img {
border: 1px solid #d8d8d8;
width: 70%;
margin: 40px;
box-shadow: 0px 0.5px 1px #d8d8d8;
}
#product-image {
margin: auto;
width: 40%;
float: left;
padding: 0;
/* border: 1px solid black; */
}
#product-info {
margin: 0;
width: 60%;
/* height: 100%; */
float: left;
padding: 0;
position: relative;
/* border: 1px solid black; */
}
.allmyText {
position: absolute;
bottom: 0;
}
.color-box {
width: 40px;
height: 40px;
margin-top: 5px;
}
.cart {
margin-right: 25px;
float: left;
border: 1px solid #d8d8d8;
padding: 5px 20px;
position: relative;
}
button {
margin-top: 30px;
border: none;
background-color: #1e95ea;
color: white;
height: 40px;
width: 200px;
font-size: 14px;
}
.disabledButton {
background-color: #d8d8d8;
}
.review-form {
width: 400px;
padding: 20px;
margin: 40px;
border: 1px solid #d8d8d8;
}
input[type=text] {
width: 100%;
height: 25px;
margin-bottom: 20px;
}
textarea {
width: 100%;
height: 60px;
}
.tab {
margin-left: 20px;
cursor: pointer;
}
.activeTab {
color: #16c0b0;
text-decoration: underline;
}
.outofStock {
text-decoration: line-through;
}
h2 {
text-align: left;
vertical-align: top;
}
解决方案
您的mounted
功能在该computed
部分内。因此,它将被视为计算属性并且永远不会被调用。
mounted
尝试在函数中放置一些控制台日志记录。您应该看到它从未被调用过。
如果您将该mounted
功能移动为顶级组件选项,它应该可以正常运行。
正如评论中已经指出的那样,您似乎也没有在eventBus
任何地方定义。如果是这种情况,我会认为您会在控制台中收到错误消息。也许您只是从问题中省略了它?
如果不是,那么这也表明这eventBus.$emit
条线永远不会被击中。我建议添加更多控制台日志记录以准确确定正在运行和未运行的行。缩小问题的范围应该不难。
推荐阅读
- github - 代码在 VSCode 中编译,但不在 ubuntu 服务器上
- javascript - 如何调整图像大小以适合容器下方的剩余窗口?
- visual-studio-code - Prettier 格式无法在 Mac 上使用最新的 prettier 扩展版本 (v7.1.0)
- swift - 从粘贴板读取后的 NSTextAttachment 内容属性 nil
- c++ - C++构造函数异常安全问题
- css - 如何更改导航栏的背景颜色?
- python - 使用 Seaborn 绘制密度图
- google-cloud-ml - 顶点管道:CustomPythonPackageTrainingJobRunOp 不提供 WorkerPoolSpecs
- android - 如何使用 google sheet 和 android studio 制作完整的 crud?
- node.js - 数据未显示在车把上