首页 > 解决方案 > 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;
}

标签: vue.js

解决方案


您的mounted功能在该computed部分内。因此,它将被视为计算属性并且永远不会被调用。

mounted尝试在函数中放置一些控制台日志记录。您应该看到它从未被调用过。

如果您将该mounted功能移动为顶级组件选项,它应该可以正常运行。

正如评论中已经指出的那样,您似乎也没有在eventBus任何地方定义。如果是这种情况,我会认为您会在控制台中收到错误消息。也许您只是从问题中省略了它?

如果不是,那么这也表明这eventBus.$emit条线永远不会被击中。我建议添加更多控制台日志记录以准确确定正在运行和未运行的行。缩小问题的范围应该不难。


推荐阅读