This is a template for working with Vue.js with the Bootstrap framework pre-installed.
A Pen by Raman Prasad on CodePen.
| <div id="app"> | |
| <nav class="navbar navbar-light fixed-top"> | |
| <div class="navbar-text ml-auto d-flex"> | |
| <button class="btn btn-sm btn-outline-success" | |
| @click="sliderStatus = !sliderStatus"> | |
| <i class="fas fa-dollar-sign"></i></button> | |
| <div class="dropdown ml-2" v-if="cart.length>0"> | |
| <button class="btn btn-success btn-sm dropdown-toggle" | |
| id="cartDropdown" data-toggle="dropdown" | |
| aria-haspopup="true" aria-expanded="false"> | |
| <span class="badge badge-pill badge-light">{{cartQty}}</span> | |
| <i class="fas fa-shopping-cart mx-2"></i> | |
| {{ cartTotal | currency }} | |
| </button> | |
| <div class="dropdown-menu dropdown-menu-right" | |
| aria-labelledby="cartDropdown"> | |
| <div v-for="(item, index) in cart" :key="index"> | |
| <div class="dropdown-item-text text-nowrap text-right"> | |
| <span class="badge badge-pill badge-warning align-text-top mr-1">{{item.qty}}</span> | |
| {{item.product.name}} | |
| <b>{{(item.qty * item.product.price) | currency}}</b> | |
| <a href="#" @click.stop="deleteItem(index)" | |
| class="badge badge-danger text-white">-</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <h1>My Shop</h1> | |
| <transition name="fade"> | |
| <div v-if="sliderStatus"> | |
| <div class="align-items-center" :class="sliderState"> | |
| <label :class="labelArr" for="formMax">max</label> | |
| <input type="text" id="formMax" class="form-control mx-2" | |
| :style="{'width': inputWidth + 'px', textAlign:'center'}" v-model="maximum"> | |
| <input type="range" class="custom-range" min="0" max="200" v-model="maximum"> | |
| </div> | |
| </div> | |
| </transition> | |
| <transition-group name="fade" tag="div" | |
| @beforeEnter="beforeEnter" | |
| @enter="enter" | |
| @leave="leave"> | |
| <div class="row d-none mb-3 align-items-center" | |
| v-for="(item, index) in products" :key="item.id" | |
| v-if="item.price<=Number(maximum)" | |
| :data-index="index"> | |
| <div class="col-1 m-auto"> | |
| <button class="btn btn-info" | |
| v-on:click="addItem(item)">+</button> | |
| </div> | |
| <div class="col-4"> | |
| <img class="img-fluid d-block" :src="item.image" :alt="item.name"> | |
| </div> | |
| <div class="col"> | |
| <h3 class="text-info">{{ item.name }}</h3> | |
| <p class="mb-0">{{ item.description }}</p> | |
| <div class="h5 float-right"><price></price><price></price></div> | |
| </div> | |
| </div> | |
| </transition-group> | |
| </div> |
| Vue.component('price', { | |
| data: function(){ | |
| return { | |
| prefix: '$', | |
| value: 22.34, | |
| precision: 2 | |
| } | |
| }, | |
| template: `<span> | |
| {{ this.prefix + | |
| Number.parseFloat(this.value).toFixed(this.precision) }} | |
| </span>` | |
| }); | |
| var app = new Vue({ | |
| el: '#app', | |
| data: { | |
| inputWidth: 60, | |
| sliderStatus: false, | |
| labelArr: ['font-weight-bold', 'mr-2'], | |
| maximum: 99, | |
| products: null, | |
| cart: [] | |
| }, | |
| filters: { | |
| currency: function(value) { | |
| return '$' + Number.parseFloat(value).toFixed(2); | |
| } | |
| }, | |
| computed: { | |
| cartTotal: function() { | |
| let sum = 0; | |
| for (key in this.cart) { | |
| sum = sum+(this.cart[key].product.price * this.cart[key].qty); | |
| } | |
| return sum; | |
| }, | |
| cartQty: function() { | |
| let qty = 0; | |
| for (key in this.cart) { | |
| qty = qty + this.cart[key].qty; | |
| } | |
| return qty; | |
| }, | |
| sliderState: function() { | |
| return this.sliderStatus ? 'd-flex': 'd-none' | |
| } | |
| }, | |
| methods: { | |
| beforeEnter: function(el) { | |
| el.className='d-none' | |
| }, | |
| enter: function(el) { | |
| var delay=el.dataset.index * 100; | |
| setTimeout(function() { | |
| el.className='row d-flex mb-3 align-items-center animated fadeInRight' | |
| }, delay); | |
| }, | |
| leave: function(el) { | |
| var delay=el.dataset.index * 100; | |
| setTimeout(function() { | |
| el.className='row d-flex mb-3 align-items-center animated fadeOutRight' | |
| }, delay); | |
| }, | |
| addItem: function(product) { | |
| var whichProduct; | |
| var existing = this.cart.filter(function(item, index) { | |
| if (item.product.id == Number(product.id)) { | |
| whichProduct = index; | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| }); | |
| if (existing.length) { | |
| this.cart[whichProduct].qty++ | |
| } else { | |
| this.cart.push({product: product, qty: 1}) | |
| } | |
| }, | |
| deleteItem: function(id) { | |
| if(this.cart[id].qty>1) { | |
| this.cart[id].qty--; | |
| } else { | |
| this.cart.splice(id, 1); | |
| } | |
| } | |
| }, | |
| mounted: function() { | |
| fetch('https://hplussport.com/api/products/order/price') | |
| .then(response => response.json()) | |
| .then(data => { | |
| this.products = data; | |
| }) | |
| } | |
| }); |
| <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"></script> | |
| <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> |
| .fade-enter, .fade-leave-to { | |
| opacity: 0; | |
| } | |
| .fade-enter-active, .fade-leave-active { | |
| transition: all 1s ease-in-out; | |
| } |
| <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" /> | |
| <link href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" rel="stylesheet" /> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css" rel="stylesheet" /> |
This is a template for working with Vue.js with the Bootstrap framework pre-installed.
A Pen by Raman Prasad on CodePen.