Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ralitsavoronevska/5a87184434d73aa3ed0686e610477200 to your computer and use it in GitHub Desktop.
Save ralitsavoronevska/5a87184434d73aa3ed0686e610477200 to your computer and use it in GitHub Desktop.
My first Vue 2 codepen [Vue Mastery: Into to Vue 2]
<!-- To learn more about this Vue codepen go to: https://www.vuemastery.com/courses/intro-to-vue-js/vue-instance -->
<template>
<div id="app">
<div class="nav-bar"></div>
<div class="cart">
<p>Cart({{ cart.length }})</p>
</div>
<product
:premium="premium"
@add-to-cart="updateCart"
@remove-from-cart="removeItem"
></product>
</div>
</template>
<script>
var eventBus = new Vue();
Vue.component("product", {
props: {
premium: {
type: Boolean,
required: true
}
},
template: `
<div class="product">
<div class="product-image">
<img :src="image" :alt="altText" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<h2>{{ description }}</h2>
<a :href="link" target="_blank">More products like this</a>
<!-- <p v-if="inventory > 10">In Stock</p>
<p v-else-if="inventory <= 10 && inventory > 0">Almost sold out!</p>
<p v-else :class="{ outOfStock: !inStock }">Out of Stock</p> -->
<p v-if="inStock">In Stock</p>
<p v-else :class="{ outOfStock: !inStock }">Out of Stock</p>
<info-tabs :shipping="shipping" :details="details"></info-tabs>
<!-- <p>{{ sale }}</p> -->
<div class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{ backgroundColor: variant.variantColor }"
@mouseover="updateProduct(index)">
</div>
<ul class="product-info-sizes">
<li v-for="size in sizes">{{ size }}</li>
</ul>
<div class="cart-btns">
<button
@click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>Add to cart
</button>
<button @click="removeFromCart" class="btn-remove-from-cart">Remove from cart</button>
</div>
<product-tabs :reviews="reviews"></product-tabs>
</div>
</div>
`,
data() {
return {
brand: "Vue Mastery",
product: "Socks",
description: "A pair of warm, fuzzy socks",
selectedVariant: 0,
altText: "A pair of socks",
link:
"https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=socks",
inventory: 100,
onSale: true,
details: ["80% cotton", "20% polyester", "Gender-neutral"],
variants: [
{
variantId: 2234,
variantColor: "green",
variantImage:
"https://www.vuemastery.com/images/challenges/vmSocks-green-onWhite.jpg",
variantQuantity: 10
},
{
variantId: 2235,
variantColor: "blue",
variantImage:
"https://www.vuemastery.com/images/challenges/vmSocks-blue-onWhite.jpg",
variantQuantity: 0
}
],
sizes: ["S", "M", "L", "XL", "XXL", "XXXL"],
cart: 0,
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;
},
sale() {
if (this.onSale) {
return this.brand + " " + this.product + " are on sale!";
} else {
return this.brand + " " + this.product + " are not on sale!";
}
},
shipping() {
if (this.premium) {
return "Free";
} else {
return 2.99;
}
}
},
mounted() {
eventBus.$on("review-submitted", (productReview) => {
this.reviews.push(productReview);
});
}
});
Vue.component("product-review", {
template: `
<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>
<p>
<label for="name">Name: </label>
<input id="name" v-model="name">
</p>
<p>
<label for="review">Review: </label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<!-- Vuejs button [Codepen]: https://codepen.io/yanxyz/pen/pyOQMy?editors=1111 -->
<p>Would you recommend this product?</p>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" v-model="recommendation" value="Yes">Yes
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" v-model="recommendation" value="No">No
</div>
<span>value: {{ recommendation }}</span>
<p>
<input type="submit" value="Submit">
</p>
</form>
`,
data() {
return {
name: null,
review: null,
rating: null,
recommendation: null,
errors: []
};
},
methods: {
onSubmit() {
if (this.name && this.review && this.rating && this.recommendation) {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating,
recommendation: this.recommendation
};
eventBus.$emit("review-submitted", productReview);
this.name = null;
this.review = null;
this.rating = null;
this.recommendation = 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-tabs", {
props: {
reviews: {
type: Array,
required: false
}
},
template: `
<div class="product-tabs-container">
<ul class="nav nav-tabs">
<li class="nav-item product-tabs" v-for="(tab, index) in tabs">
<a class="nav-link" aria-current="page"
:class="{active: selectedTab === tab}"
@click="selectedTab = tab"
:key="tab">{{ tab }}</a>
</li>
</ul>
<div class="product-tabs-reviews-container" v-show="selectedTab === 'Reviews'">
<h2>Reviews</h2>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
<p>Would you recommend this product? Answer: {{ review.recommendation }}</p>
</li>
</ul>
</div>
<product-review class="product-tabs-make-a-review-container" v-show="selectedTab === 'Make a Review'"></product-review>
</div>
`,
data() {
return {
tabs: ["Reviews", "Make a Review"],
selectedTab: "Reviews"
};
}
});
Vue.component("info-tabs", {
props: {
shipping: {
required: true
},
details: {
type: Array,
required: true
}
},
template: `
<div>
<ul class="nav nav-tabs info-tabs">
<li class="nav-item" v-for="(tab, index) in tabs">
<a class="nav-link" aria-current="page" href="#"
:class="{active: selectedTab === tab}"
@click="selectedTab = tab"
:key="tab">{{ tab }}</a>
</li>
</ul>
<div v-show="selectedTab === 'Shipping'" class="info-tabs-shipping-content">
<p>{{ shipping }}</p>
</div>
<div v-show="selectedTab === 'Details'" class="info-tabs-details-content">
<ul class="info-tabs-details">
<li v-for="detail in details">{{ detail }}</li>
</ul>
</div>
</div>
`,
data() {
return {
tabs: ["Shipping", "Details"],
selectedTab: "Shipping"
};
}
});
export default {
data: {
premium: true,
cart: []
},
methods: {
updateCart(id) {
this.cart.push(id);
},
removeItem(id) {
for (var i = this.cart.length - 1; i >= 0; i--) {
if (this.cart[i] === id) {
this.cart.splice(i, 1);
}
}
}
}
};
</script>
<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style>
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 0.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;
display: inline-flex;
}
.color-box:not(:first-child) {
margin-left: 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;
}
ul.info-tabs {
padding: 0 !important;
}
ul.info-tabs-details,
ul.product-info-sizes {
padding-left: 15px;
}
ul.product-info-sizes {
margin-top: 15px;
}
.tabs:not(:first-child) {
margin-left: 20px;
cursor: pointer;
}
.activeTab {
color: #16c0b0;
text-decoration: underline;
}
.outOfStock {
text-decoration: line-through;
}
.btn-remove-from-cart {
width: 150px;
}
.nav-tabs .nav-item.show .nav-link,
.nav-tabs .nav-link.active {
color: #fff;
background-color: #fff;
border-color: #16c0b0;
background: linear-gradient(-90deg, #84cf6a, #16c0b0);
}
.nav-tabs {
border-bottom: 1px solid #16c0b0;
}
.info-tabs-shipping-content,
.info-tabs-details-content {
margin-top: 15px;
}
.product-tabs-container,
.product-tabs-reviews-container,
.product-tabs-make-a-review-container {
margin-top: 15px;
}
.product-tabs {
display: inline-block !important;
}
.nav-link,
.nav-link:hover {
color: #000000;
}
.nav-tabs .nav-link:hover {
border: 1px solid #16c0b0;
color: #16c0b0;
}
.nav-tabs .nav-link.active:hover {
color: #000;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment