How can I add multiple product in cart using vue js and django rest framework - vue.js

I am trying to add multiple product in the cart. I am using vuejs and django rest framework. My problem is: When I add a product into the cart it added successfully But when I add another product it doesnt add. It adds the same product again and again.
For example:
I have three products named "A" and "B" and "C". I added "A". Then i try to add "C" in the cart. But it stills adds "A" in the cart. I cleared the session and tried again still add the "A" product if I try to add "C" product first. It always add "A" product.
Here is my store/index.js:
import { createStore } from 'vuex'
export default createStore({
state: {
cart: {
items: []
},
isAuthenticated: false,
token: '',
isLoading: false,
},
mutations: {
initializeStore(state){
if(localStorage.getItem('cart')){
state.cart = JSON.parse(localStorage.getItem('cart'))
}
else{
localStorage.setItem('cart', JSON.stringify(state.cart))
}
},
addToCart(state, item) {
let exists = state.cart.items.filter(i => i.product.id === item.product.id)
if (exists.length){
exists[0].quantity = parseInt(exists[0].quantity) + parseInt(item.quantity)
}
else{
state.cart.items.push(item)
}
localStorage.setItem('cart', JSON.stringify(state.cart))
}
},
actions: {
},
modules: {
}
})
Here is my add to cart page and code:
<template>
<br />
<div class="col">
<div class="col1">
<img v-bind:src="product.get_image" alt="">
</div>
<div class="col2">
<div class="product__title">
<p>PRODUCT TITLE</p>
<h2>{{ product.name }}</h2>
<small>{{ product.short_description }}</small>
</div>
<div class="product__price">
<p>Price: $ {{ product.price }}</p>
</div>
<div class="product__button">
<input type="hidden" v-model="quantity" min="1">
<button type="submit" class="button__primary" #click="addToCart">Add To Cart</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import {toast} from 'bulma-toast'
export default {
name: "ProductDetail",
data () {
return {
product: {},
quantity: 1
}
},
mounted() {
this.getProduct()
},
methods: {
getProduct() {
const categorySlug = this.$route.params.category_slug
const productSlug = this.$route.params.product_slug
axios.get(`/api/product-details/${categorySlug}/${productSlug}/`)
.then(response => {
this.product = response.data
})
.catch(error => {
console.log(error)
})
},
addToCart() {
if(isNaN(this.quantity) || this.quantity < 1){
this.quantity = 1
}
const item = {
product: this.product,
quantity: this.quantity
}
this.$store.commit("addToCart", item)
toast({
message: "Product has been added to cart" + item.product.name,
type: "is-success",
pauseOnHover: true,
duration: 2000,
position: "bottom-right",
dismissible: true,
})
}
}
}
</script>
<style scoped>
.col {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 5rem;
height: 50vh;
padding: 60px;
}
.col1 {
height: 520px;
width: 100%;
}
.col1 img{
object-fit: fill;
height: 520px;
width: 100%;
}
.col2 {
display: grid;
grid-auto-rows: 1fr 1fr 1fr 1fr;
background: #fff;
padding: 12px;
}
.product__button button{
height: 40px;
width: 100%;
border: none;
background: #007bc4;
color: #fff;
border-radius: 3px;
}
.product__price p {
color: #007bc4;
font-size: 18px;
}
</style>
Here is the cartitem component:
<template>
<tr class="is-fullwidth">
<td>{{ item.product.name }}</td>
<td>$ {{ item.product.price }}</td>
<td>
<button #click="increment(item)" class="plusButton">+</button>
{{ item.quantity }}
<button #click="decrement(item)" class="minusButton">-</button>
</td>
<td>{{ getTotal(item).toFixed(2) }}</td>
<td><button class="delete"></button></td>
</tr>
</template>
<script>
export default {
name: "Cartitem",
props: {
initialItem: Object
},
data() {
return {
item: this.initialItem
}
},
methods: {
getTotal(item) {
return item.quantity * item.product.price
},
increment(item){
item.quantity += 1
this.updateCart()
},
decrement(item) {
item.quantity -= 1
if(item.quantity === 0){
this.$emit('removeFromCart', item)
}
this.updateCart()
},
updateCart() {
localStorage.setItem('cart', JSON.stringify(this.$store.state.cart))
},
removeFromCart(item) {
this.$emit('removeFromCart', item)
this.updateCart()
}
}
}
</script>
<style scoped>
.plusButton{
border: none;
background: #fff;
font-size: 19px;
}
.minusButton{
border: none;
background: #fff;
font-size: 19px;
}
</style>
I am new in vue js. I am trying to build this projetc so that i can learn. But this issue is eating my brain. I tried to use find function in sotre/index.js. It solved my problem though but if i clear the cookies and try to add product it gives me error.
is there any solution for me?
Thanks in advance.

Related

Component not rendering nor providing an error

I have a table in a component, which I render into another component however, when I render the component inside of another component, it is not appearing, and due to the complete lack of errors I can't even proceed. I've checked how I've imported etc and it seems correct. But perhaps someone can spot something I haven't. I'm very new to VueJs so apologies for the untrimmed code, I'm not 100% on what could be relevant yet.
File I'm Importing Into.vue
<template>
<div>
<b-table
:striped="striped"
:bordered="false"
:data="participants"
detailed
class="participant-table"
:row-class="() => 'participant-row'"
>
<InternalTable></InternalTable>
<b-table-column field="columnValue" v-slot="props2" class="attr-column">
<b-table
:bordered="false"
class="attr-table"
:striped="true"
:data="props2.row.columnValues"
>
<b-table-column field="columnName" v-slot="itemProps">
<SelectableAttribute
:attr-name="props2.row.fieldClass"
:attr-id="itemProps.row.id"
:model-id="itemProps.row.id"
model-name="NewParticipant"
>
{{ itemProps.row.value }}
</SelectableAttribute>
</b-table-column>
</b-table>
</b-table-column>
</b-table>
</div>
</template>
<script>
import { snakeCase } from "snake-case";
import InternalTable from './InternalTable'
import SelectableAttribute from '../groups/SelectableAttribute'
export default {
props: {
bordered: true,
striped: true,
participants: [
{
primaryAlias: '',
primaryEmail: '',
primaryAddress: '',
primaryPhone: '',
}
]
},
},
components: {
SelectableAttribute,
InternalTable
},
methods: {
tableDataToDataValueCells(participant) {
const fields = [
{ fieldName: 'companyNames', fieldClass: 'CompanyName' },
{ fieldName: 'aliases', fieldClass: 'Alias' },
{ fieldName: 'addresses', fieldClass: 'Address' },
{ fieldName: 'phones', fieldClass: 'Phone' },
{ fieldName: 'emails', fieldClass: 'Email' },
{ fieldName: 'birthdates', fieldClass: 'Birthdate' },
{ fieldName: 'customerNumbers', fieldClass: 'CustomerNumber' },
{ fieldName: 'ibans', fieldClass: 'BankAccount' },
];
let result = [];
fields.forEach(field => {
if (participant[field.fieldName].length > 0) {
result.push({
attributeName: field.fieldName,
columnName: I18n.t(`ccenter.participant.table.${snakeCase(field.fieldName)}`),
columnValues: participant[field.fieldName],
fieldClass: field.fieldClass,
})
}
});
return result;
}
}
}
</script>
<style>
.table tbody tr.detail:last-child td {
border-width: 1px !important;
}
.participant-table .participant-row td {
word-break: break-word;
font-size: 13px;
padding: 10px 5px;
}
.cell-action {
padding-left: 0px !important;
padding-right: 0px !important;
}
.cell-action .b-radio {
margin: 0px;
}
.attr-table table thead {
display: none;
}
.attr-table table td {
border: none;
}
.attrs-detail-container table tr td:nth-child(2) {
padding: 0 !important;
}
.attrs-detail-container table thead {
display: none;
}
</style>
InternalTable.Vue
<template>
<b-table :data="participants" detailed class="participant-table" :row-class="() => 'participant-row'">
<b-table-column field="primaryAlias" :label="t('participant.table.primary_alias')" v-slot="props">
<template v-if="props.row.primaryAlias">{{ props.row.primaryAlias.value }}</template>
<template v-else>-</template>
</b-table-column>
<b-table-column field="primaryEmail" :label="t('participant.table.primary_email')" v-slot="props">
<template v-if="props.row.primaryEmail">{{ props.row.primaryEmail.value }}</template>
<template v-else>-</template>
</b-table-column>
<b-table-column field="primaryAddress" :label="t('participant.table.primary_address')" v-slot="props">
<template v-if="props.row.primaryAddress">{{ props.row.primaryAddress.value }}</template>
<template v-else>-</template>
</b-table-column>
<b-table-column field="primaryPhone" :label="t('participant.table.primary_phone')" v-slot="props">
<template v-if="props.row.primaryPhone">{{ props.row.primaryPhone.value }}</template>
<template v-else>-</template>
</b-table-column>
<b-table-column v-slot="props" cell-class="cell-action">
<slot v-bind="props.row">
</slot>
</b-table-column>
<template slot="detail" slot-scope="props">
<b-table class="attrs-detail-container" :data="tableDataToDataValueCells(props.row)" cell-class="with-bottom-border">
<b-table-column field="columnName" v-slot="props">
<b>{{ props.row.columnName }}</b>
</b-table-column>
</b-table>
</template>
</b-table>
</template>
<script>
import { snakeCase } from "snake-case"
export default {
props: {
participants: {
type: Array,
}
},
methods: {
tableDataToDataValueCells(participant) {
const fields = [
{ fieldName: 'companyNames', fieldClass: 'CompanyName' },
{ fieldName: 'aliases', fieldClass: 'Alias' },
{ fieldName: 'addresses', fieldClass: 'Address' },
{ fieldName: 'phones', fieldClass: 'Phone' },
{ fieldName: 'emails', fieldClass: 'Email' },
{ fieldName: 'birthdates', fieldClass: 'Birthdate' },
{ fieldName: 'customerNumbers', fieldClass: 'CustomerNumber' },
{ fieldName: 'ibans', fieldClass: 'BankAccount' },
];
let result = [];
fields.forEach(field => {
if (participant[field.fieldName].length > 0) {
result.push({
attributeName: field.fieldName,
columnName: I18n.t(`ccenter.participant.table.${snakeCase(field.fieldName)}`),
columnValues: participant[field.fieldName],
fieldClass: field.fieldClass,
})
}
});
return result;
}
}
};
</script>
<style scoped>
.table tbody tr.detail:last-child td {
border-width: 1px !important;
}
.participant-table .participant-row td {
word-break: break-word;
font-size: 13px;
padding: 10px 5px;
}
.cell-action {
padding-left: 0px !important;
padding-right: 0px !important;
}
.cell-action .b-radio {
margin: 0px;
}
.attr-table table thead {
display: none;
}
.attr-table table td {
border: none;
}
.attrs-detail-container table tr td:nth-child(2) {
padding: 0 !important;
}
.attrs-detail-container table thead {
display: none;
}
</style>
What I see is that you trying to use props in your InternalTable.vue, which you used like this:
props: {
participants: {
type: Array,
}
}
In your Into.vue, you just call your component without providing these props. You should change:
<InternalTable></InternalTable>
to
<InternalTable :participants="participants"></InternalTable>
Looks like your b-table isn´t rendering because you didn´t provide data.
EDIT: Your data needs to be filled first
Currently, your array participants is empty, it just consists of:
participants: {
type: Array,
default: null,
}
In your InternalTable.vue you refer to primaryAlias, primaryEmail, primaryAddress and primaryPhone at your b-table-column´s. This data isn´t provided yet, thats why the table renders without data. You need to provide an array with a minimum structure of:
participants: [
{
primaryAlias: '',
primaryEmail: '',
primaryAddress: '',
primaryPhone: ''
}
]

Assing uploaded file value to input field multiple times using vue.js

Assign filename to input field on upload and then show success message working fine,but when i try to delete file and upload again it's not working!
new Vue({
el: "#app",
data() {
return {
form: {
message: '',
fileurl: ''
},
loading: false
}
},
methods: {
uploadImage(event) {
this.form.fileurl = 'uploaded!'
},
deleteFile(furl) {
this.form.fileurl = ''
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
span {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h3>
File Upload / remove Demo
</h3>
<hr />
<div class="custom-file attach_file" v-show="!form.fileurl">
<input type="file" id="file" name="file" #change="uploadImage($event)">
<input type="text" v-model="form.fileurl">
</div>
<p v-if="form.fileurl"> {{ form.fileurl }} <span #click="deleteFile(form.fileurl)">Delete</span></p>
</div>
I am not getting any console error as well.
This is what i have tried so far.
Can you guys please have a look at this!
The problem is that #change is not trigger when you choose the same file. The simplest solution is reset value of input when you click delete
this.$refs.fileToUpload.value = '';
new Vue({
el: "#app",
data() {
return {
form: {
message: '',
fileurl: ''
},
loading: false
}
},
methods: {
uploadImage(event) {
this.form.fileurl = 'uploaded!'
},
deleteFile(furl) {
this.form.fileurl = ''
this.$refs.fileToUpload.value = '';
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
span {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h3>
File Upload / remove Demo
</h3>
<hr />
<div class="custom-file attach_file" v-show="!form.fileurl">
<input type="file" id="file" name="file" class="custom-file-input" #change="uploadImage($event)" ref="fileToUpload">
<input type="text" v-model="form.fileurl">
</div>
<p v-if="form.fileurl"> {{ form.fileurl }} <span #click="deleteFile(form.fileurl)">Delete</span></p>
</div>
You need to clear you input field first.
Update your upload method with this.
uploadImage(event) {
this.form.fileurl = 'uploaded!'
this.success = false
this.$nextTick(() => {
this.success = true
})
Have a look at this working demo
https://jsfiddle.net/asimshahzad/8c9pcfys/

Vue.js 2.0 data binding issue - one data field's change will trigger the other data fields' filter

Following is the code, I define a my data in Vue as below:
{
result: {
name: 'user A',
age: 20,
male: true
},
showDetails: true
}
There are several filter/computed/methods in the view will do the result formatting.
The problem is even I just change the value of the showDetailds, the methods and filters defined for the result will be triggered as well.
May I know any issue in my code ? Or it's the natural of Vue.js (I don't believe.) ?
function countTheCall(key) {
let counter = document.getElementById(key)
if (!counter) {
let container = document.getElementById('statList')
var item = document.createElement('li')
item.innerHTML = `${key}: <span id='${key}'>0</span>`
container.appendChild(item)
counter = document.getElementById(key)
}
counter.innerText = _.parseInt(counter.innerText) + 1
}
var vm = new Vue({
el: '#app',
data() {
return {
result: {
name: 'user A',
age: 20,
male: true
},
showDetails: true
}
},
methods: {
countTheCall: function(key) {
let counter = document.getElementById(key)
counter.innerText = _.parseInt(counter.innerText) + 1
},
trimValue: function(value) {
console.log('Invoke method - trimValue.')
countTheCall('methodCalls of trimValue')
return _.trim(value)
},
getValFromResult: function(key) {
console.log('Invoke method - getValFromResult.')
countTheCall('methodCalls of getValFromResult')
return _.get(this.result, key, 'null')
}
},
computed: {
displayName: function() {
console.log('Invoke computed value - computedVal.')
countTheCall('computedCalls of displayName')
return `${this.result.name} (${this.result.age})`
}
},
filters: {
convertString: function(val) {
countTheCall('filterCalls of convertString')
return _.upperFirst(val)
},
getValFromObject: function(obj, key) {
console.log(`[filter] getValue From Object by ${key}`)
countTheCall('filterCalls of getValFromObject')
return _.get(obj, key, 'null')
},
convertDateString: function(datestr, format = 'MMM DD (ddd), YYYY') {
console.log(`[filter] convertDateString ${datestr}`)
countTheCall('filterCalls of convertDateString')
let m = moment(datestr)
let formattedStr = ''
if (m.isValid()) {
formattedStr = m.format(format)
}
return formattedStr
}
}
})
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
label {
font-style: italic;
color: black;
}
span {
font-weight: bold;
color: darkblue;
}
.callStat {
float: right;
width: 400px;
border: solid darkgrey 2px;
padding: 5px;
margin-right: 40px;
}
<body>
<script type="text/javascript" src="//cdn.bootcss.com/vue/2.2.4/vue.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/moment.js/2.17.1/moment-with-locales.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
<div class="callStat">
<h4>The Stat. of Calls</h4>
<ul id="statList"></ul>
</div>
<div id="app">
<h2>User Profile</h2>
<div>
<label>User Name:</label>
<input type="text" v-model="result.name"></input>
</div>
<div>
<label>Show Details:</label>
<input type="checkbox" id="showDetails" v-model="showDetails"></input>
<label for="showDetails">show detail section?</label>
</div>
<hr/>
<div>
<label>User Name:</label>
<ul>
<li>result.name: <span>{{trimValue(result.name)}}</span></li>
<li>result.unknownFiled: <span>{{result.unknownFiled}}</span></li>
<li>getValFromResult('name'): <span>{{ getValFromResult('name')}} </span></li>
<li>getValFromResult('unknownField'): <span>{{ getValFromResult('unknownField')}} </span></li>
<li>computed Display Name : <span>{{ displayName }} </span></li>
<li>convertString by filter : <span>{{ result.name | convertString }} </span></li>
<li>user birthDay: <span>{{ result.birthDay | convertDateString}}</span></li>
</ul>
</div>
</div>
</body>
This is natural to Vue.
Methods & Filters run on every DOM re-render.
This is why it's best to use Computed whenever possible - these re-run only on change of connected properties.

Vuejs Ajax call and dataTable

I'm using Vuejs and dataTable for one of my project. I make an Ajax call and push data into an array. After that I use v-for to loop the data in the <tr> tag. Most of the time it doesn't work. The table loads as soon as the page has finished loading.. it takes a bit of time to receive the ajax data. Here is the output. It says no data available in the table
So the serch option doesn't work properly. I thought to use a setTimeout function (which was a bad idea) to load the table after a bit of time. What would be the proper way to do it? Sharing the code :
new Vue({
el: '#app',
data: {
entries: [],
},
methods:{
getData(){
var route = '/admin/temporary-enrolled-students';
this.$http.get(route).then((response)=>{
for(var i = 0; i< response.data.length;i++)
{
this.entries.push({
scId: response.data[i].id,
name: response.data[i].user.name,
phone: response.data[i].user.phone,
email: response.data[i].user.email,
courseId: response.data[i].course.id,
courseName: response.data[i].course.course_title,
expiryDate: response.data[i].expiry_date,
shares: response.data[i].number_of_shares,
expired: (response.data[i].expired == 1),
enrollDate: response.data[i].created_at
})
}
})
},
},
mounted(){
this.getData();
},
});
//data table
$(function () {
setTimeout(()=> {
$("#temp-enroll").DataTable({
"paging": true,
"ordering": false,
"info": true,
"autoWidth": false
});
},1000);
});
in blade:
Ok I tried this and working exactly what I have wanted. Thanks everyone for supporting.
new Vue({
el: '#app',
data: {
entries: [],
},
methods:{
getData(){
var route = '/admin/temporary-enrolled-students';
this.$http.get(route).then((response)=>{
for(var i = 0; i< response.data.length;i++)
{
this.entries.push({
scId: response.data[i].id,
name: response.data[i].user.name,
............................
......................
enrollDate: response.data[i].created_at
})
}
}).then(()=>{
$("#temp-enroll").DataTable({
"paging": true,
"ordering": false,
"info": true,
"autoWidth": false
});
});
},
},
mounted(){
this.getData();
},
});
I think someone mentioned that a computed property is the way to go, and that is what I would recommend as well.
The reason why is that as soon as your template makes a reference to the computed property, this triggers your ajax call. The page will then render any other available elements, and when your ajax call returns, it will then render the data it received. No special action in code is required.
Anyway, here is what it would look like...
In your html, as an example...
<table>
<tr v-for="item in serverData">
<td>{{item.name}}</td>
<td>{{item.someOtherValue}}</td>
</tr>
</table>
export default {
name: 'pets',
data () {
return {
}
},
computed: {
serverData: function() {
// ajax call using axios - returns a json array []
axios.get("http://localhost:8080/getSomeData")
.success(function(data) {
return data;
};
}
}
}
Notice the return in the success promise returned from the ajax call - there is no need to store the data to another variable (and you should definitely not have to loop through it like the noob coder example above shows. Do that on the server, return an array ready to render - if it isn't your web service, then write your own that calls the original and do the data manipulation there).
The parts of your template that reference the computed value will render after the data arrives - there is no need to watch it.
Hope this helps!
Vue is the bomb!
<template>
<div class="panel panel-default tablecontainer">
<div class="panel-heading">ChartBookin Import</div>
<br>
<div class='col-md-12'>
<div class='col-md-3'></div>
<div class='col-md-3'>
<div class="panel panel-default">
<div class="panel-body">
<commitChart :width="150"
:height="150"></commitChart>
</div>
</div>
</div>
<div class='col-md-3'>
<div class="panel panel-default">
<div class="panel-body">
<InboxMessage :width="150"
:height="150"></InboxMessage>
</div>
</div>
</div>
</div>
<div class="panel-body">
<div class='col-md-3'>
<label> by Account </label>
<select v-model="teamId" class="form-control" rows="3">
<option VALUE="" disabled> CHOISIR UN TEAM</option>
<option v-for="option in options" v-bind:value="option.id">{{option.name}}</option>
</select>
</div>
<div class='col-md-3'>
<label> by Date</label>
<div class="form-group">
<input type="text" class="form-control" name="daterange"
value="01/01/2017-01/31/2017"/>
</div>
</div>
<div class='col-md-5'></div>
<div class='col-md-1'>
<label>Records</label>
<div class="form-group">
<select v-model="elementsPerPage" class="form-control">
<option v-for="option in filtre" v-bind:value="option">
{{ option }}
</option>
</select>
</div>
</div>
<div id="sixthTable">
<table class="table-bordered table-striped table-bordered table-hover">
<thead>
<tr>
<th v-for=" (key,value) in rows[0]" v-on:click="sortTable(value)">{{value}}
<div class="arrow" v-if="value == sortColumn"
v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-if=" rows.length > 0" v-for="row in rows">
<td v-for="(key, value) in row">{{ key }}</td>
<td>
<a :href="'/stat_booking_import/' + row.Id">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
</td>
</tr>
<tr v-else> No Result Founded</tr>
</tbody>
</table>
<div class="pagination">
<div id="paginatebutton">
«
<a class="" v-for="i in num_pages()"
v-bind:class="[i == currentPage ? 'active' : '']"
v-on:click="change_page(i)">{{i}}
</a>
»
</div>
<div class="col-md-12" id="paginatetexte">
<p v-if="pages > (elementsPerPage*currentPage) ">
Showing 1 to {{elementsPerPage * currentPage }} of {{ pages }} records
</p>
<p v-else>
Showing 1 to {{pages}} of {{ pages }} records
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import axios from 'axios';
import CommitChart from './Mail';
import InboxMessage from './Inbox';
export default {
components: {
CommitChart,
InboxMessage
},
data() {
return {
filtre: [10, 25, 50, 100],
option: 0,
options: [],
currentPage: 1,
elementsPerPage: 10,
pages: 0,
ascending: false,
sortColumn: '',
startdate: null,
enddate: null,
options: [],
teamId: null,
columns: [],
messages: [],
date: 0,
rows: {},
}
},
created() {
this.getData();
this.getTeams();
this.getMailInbox();
},
mounted() {
let vm = this;
$(document).ready(function () {
$('input[name="daterange"]').daterangepicker({
ranges: {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')]
},
locale: {
format: 'YYYY-MM-DD'
},
});
$('.applyBtn').click(function () {
vm.startdate = $("input[name=daterangepicker_start]").val();
vm.enddate = $("input[name=daterangepicker_end]").val();
vm.getData();
});
$('input[name="daterange"]').on('apply.daterangepicker', function (ev, picker) {
vm.startdate = $("input[name=daterangepicker_start]").val();
vm.enddate = $("input[name=daterangepicker_end]").val();
vm.getData();
});
});
},
watch: {
date: function () {
this.getData();
},
teamId: function () {
this.getData();
},
elementsPerPage: function () {
this.getData();
}
},
methods: {
getData() {
axios.get(`/admin/stat_booking_import.json/` + this.startdate + `/` + this.teamId + `/` + this.enddate + `/` + this.elementsPerPage + `?page=` + this.currentPage)
.then(response => {
this.rows = response.data.elements.data;
this.columns = Object.keys(this.rows[0]);
this.pages = response.data.elements.total_element;
})
.catch(e => {
this.errors.push(e)
})
},
getTeams() {
axios.get('/admin/team.json')
.then(response => {
this.options = response.data.data;
this.errors = [];
})
.catch(e => {
e.message = "aucun resultat trouvé essayer de choisir une autre date";
this.errors.push(e)
})
},
getMailInbox() {
axios.get(`/mailstorage.json`)
.then(response => {
this.messages = response.data.data;
console.log(this.messages);
})
.catch(e => {
this.errors.push(e)
});
},
sortTable(col) {
if (this.sortColumn === col) {
this.ascending = !this.ascending;
} else {
this.ascending = true;
this.sortColumn = col;
}
var ascending = this.ascending;
this.rows.sort(function (a, b) {
if (a[col] > b[col]) {
return ascending ? 1 : -1
} else if (a[col] < b[col]) {
return ascending ? -1 : 1
}
return 0;
})
},
num_pages() {
return Math.ceil(this.pages / this.elementsPerPage);
},
get_rows() {
var start = (this.currentPage - 1) * this.elementsPerPage;
var end = start + this.elementsPerPage;
return this.rows.slice(start, end);
},
change_page(page) {
this.currentPage = page;
this.getData();
}
},
}
</script>
<style type="text/css">
table {
width: 100%;
}
table td {
text-align: center;
}
table td {
text-align: center;
padding: 8px;
}
table td:last-child {
border-right: none;
}
.pagination {
display: inline-block;
}
.pagination a {
color: #3097D1;
float: left;
padding: 8px 16px;
text-decoration: none;
transition: background-color .3s;
border: 1px solid #ddd;
}
.pagination a.active {
background-color: #3097D1;
color: white;
border: 1px solid #3097D1;
}
.arrow_down {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAAAXNSR0IArs4c6QAAAvlJREFUSA29Vk1PGlEUHQaiiewslpUJiyYs2yb9AyRuJGm7c0VJoFXSX9A0sSZN04ULF12YEBQDhMCuSZOm1FhTiLY2Rky0QPlQBLRUsICoIN/0PCsGyox26NC3eTNn3r3n3TvnvvsE1PkwGo3yUqkkEQqFgw2Mz7lWqwng7ztN06mxsTEv8U0Aam5u7r5EInkplUol/f391wAJCc7nEAgE9Uwmkzo4OPiJMa1Wq6cFs7Ozt0H6RqlUDmJXfPIx+qrX69Ti4mIyHA5r6Wq1egND+j+IyW6QAUoul18XiUTDNHaSyGazKcZtdgk8wqhUKh9o/OMvsVgsfHJy0iWqVrcQNRUMBnd6enqc9MjISAmRP3e73T9al3XnbWNjIw2+KY1Gc3imsNHR0YV4PP5+d3e32h3K316TySQFoX2WyWR2glzIO5fLTSD6IElLNwbqnFpbWyO/96lCoai0cZjN5kfYQAYi5H34fL6cxWIZbya9iJyAhULBHAqFVlMpfsV/fHxMeb3er+Vy+VUzeduzwWC45XA4dlD/vEXvdDrj8DvURsYEWK3WF4FA4JQP9mg0WrHZbEYmnpa0NxYgPVObm5teiLABdTQT8a6vrwdRWhOcHMzMzCiXlpb2/yV6qDttMpkeshEzRk4Wo/bfoe4X9vb2amzGl+HoXNT29vZqsVi0sK1jJScG+Xx+HGkL4Tew2TPi5zUdQQt9otPpuBk3e0TaHmMDh1zS7/f780S0zX6Yni+NnBj09fUZUfvudDrNZN+GkQbl8Xi8RLRtHzsB9Hr9nfn5+SjSeWUCXC7XPq5kw53wsNogjZNohYXL2EljstvtrAL70/mVaW8Y4OidRO1/gwgbUMvcqGmcDc9aPvD1gnTeQ+0nmaInokRj0nHh+uvIiVOtVvt2a2vLv7Ky0tL3cRTXIcpPAwMDpq6R4/JXE4vFQ5FI5CN+QTaRSFCYc8vLy1l0rge4ARe5kJ/d27kYkLXoy2Jo4C7K8CZOsEBvb+9rlUp1xNXPL7v3IDwxvPD6AAAAAElFTkSuQmCC')
}
.arrow_up {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAaCAYAAACgoey0AAAAAXNSR0IArs4c6QAAAwpJREFUSA21Vt1PUmEYP4dvkQ8JFMwtBRocWAkDbiqXrUWXzU1rrTt0bdVqXbb1tbW16C9IBUSmm27cODdneoXjputa6069qwuW6IIBIdLvdaF4OAcOiGeDc87zPs/vd57P96WpFq7p6enbGo1mjKZpeTabjU1MTCRagGnOZHFxcXxtbe1XKpUq7+zslJeXl//Mz8+Hy+Uy3RxSE9qTk5M3otFooVQqgef4Wl9f343FYoEmoISrxuNxFX5f9vb2jhn/PxUKhfLS0tIPfFifUESRUMV8Pv/M6XReRm5rTGQyGeXxeGxYe1ezeBpBOBx2rKysbO7v79d4Wy3Y2Nj4GQqFbgnhaugxwiuGJx99Pp9FLBbXxYTXvTqd7v3MzIy6riIWGxJnMpl7AwMD14xGYyMsSq1WUyQdUqn0eSPlusQIsbGrq+vl4OCgvhFQZd1utyv1en0gEolcqsi47nWJlUrlG5fLZVcoFFy2nDKSDpIWlUoVTCQSEk4lCHmJMZ2GTCbTiMVikfIZ88l7enoos9l8dXt7+z6fDicxSJUokqDX6xXcl2wCROoc0vQCWL3sNfLOSdzR0fHY4XC4tVotl40gmVwup9xuN4OQv+UyqCFGH9rg7SOGYVRcBs3IEG4J0nVnamrqOtvuBDGGgQg9+wHFcVEi4a0LNkbdd6TrPKo8ODc311mteIIYjT/a398/jK+s1jnVM0kXoufCFvq0GuiIGEVgQIhfoygM1QrteEa9dAL7ITiYCt4RMabOK5AyKKzKWtvupLcRciu8D5J0EuDDPyT/Snd39yh6VtY2NhYQSR9G79Ds7OxdskRjEyAufvb7/cPoO5Z6e1+xtVKrq6vfcFzyi/A3ZrPZ3GdNSlwgo5ekE4X2RIQGf2C1WlufFE0GBeGWYQ8YERWLxQtnUVB830MKLZfL9RHir8lkssCn2G751tZWEWe03zTKm15YWPiEiXXTYDB0Ig/t7yd8PRws4EicwWHxO4jHD8/C5HiTTqd1BwcHFozKU89origB+y/kmzgYpgOBQP4fGmUiZmJ+WNgAAAAASUVORK5CYII=')
}
.arrow {
float: right;
width: 12px;
height: 15px;
background-repeat: no-repeat;
background-size: contain;
background-position-y: bottom;
}
.number {
display: inline-block;
padding: 4px 10px;
margin: 0px 5px;
cursor: pointer;
}
.number:hover,
.number.active {
background-color: #3097D1;
border-color: #3097D1;
}
#paginatetexte {
padding-top: 6%;
import Vue from 'vue'
new Vue({
el: '#statistique',
render: h => h(require('./StatBookingImport.vue'))
});
<style type="text/css">
table {
width: 100%;
}
table td {
text-align: center;
}
table td {
text-align: center;
padding: 8px;
}
table td:last-child {
border-right: none;
}
.pagination {
display: inline-block;
}
.pagination a {
color: #3097D1;
float: left;
padding: 8px 16px;
text-decoration: none;
transition: background-color .3s;
border: 1px solid #ddd;
}
.pagination a.active {
background-color: #3097D1;
color: white;
border: 1px solid #3097D1;
}
.arrow_down {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAAAXNSR0IArs4c6QAAAvlJREFUSA29Vk1PGlEUHQaiiewslpUJiyYs2yb9AyRuJGm7c0VJoFXSX9A0sSZN04ULF12YEBQDhMCuSZOm1FhTiLY2Rky0QPlQBLRUsICoIN/0PCsGyox26NC3eTNn3r3n3TvnvvsE1PkwGo3yUqkkEQqFgw2Mz7lWqwng7ztN06mxsTEv8U0Aam5u7r5EInkplUol/f391wAJCc7nEAgE9Uwmkzo4OPiJMa1Wq6cFs7Ozt0H6RqlUDmJXfPIx+qrX69Ti4mIyHA5r6Wq1egND+j+IyW6QAUoul18XiUTDNHaSyGazKcZtdgk8wqhUKh9o/OMvsVgsfHJy0iWqVrcQNRUMBnd6enqc9MjISAmRP3e73T9al3XnbWNjIw2+KY1Gc3imsNHR0YV4PP5+d3e32h3K316TySQFoX2WyWR2glzIO5fLTSD6IElLNwbqnFpbWyO/96lCoai0cZjN5kfYQAYi5H34fL6cxWIZbya9iJyAhULBHAqFVlMpfsV/fHxMeb3er+Vy+VUzeduzwWC45XA4dlD/vEXvdDrj8DvURsYEWK3WF4FA4JQP9mg0WrHZbEYmnpa0NxYgPVObm5teiLABdTQT8a6vrwdRWhOcHMzMzCiXlpb2/yV6qDttMpkeshEzRk4Wo/bfoe4X9vb2amzGl+HoXNT29vZqsVi0sK1jJScG+Xx+HGkL4Tew2TPi5zUdQQt9otPpuBk3e0TaHmMDh1zS7/f780S0zX6Yni+NnBj09fUZUfvudDrNZN+GkQbl8Xi8RLRtHzsB9Hr9nfn5+SjSeWUCXC7XPq5kw53wsNogjZNohYXL2EljstvtrAL70/mVaW8Y4OidRO1/gwgbUMvcqGmcDc9aPvD1gnTeQ+0nmaInokRj0nHh+uvIiVOtVvt2a2vLv7Ky0tL3cRTXIcpPAwMDpq6R4/JXE4vFQ5FI5CN+QTaRSFCYc8vLy1l0rge4ARe5kJ/d27kYkLXoy2Jo4C7K8CZOsEBvb+9rlUp1xNXPL7v3IDwxvPD6AAAAAElFTkSuQmCC')
}
.arrow_up {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAaCAYAAACgoey0AAAAAXNSR0IArs4c6QAAAwpJREFUSA21Vt1PUmEYP4dvkQ8JFMwtBRocWAkDbiqXrUWXzU1rrTt0bdVqXbb1tbW16C9IBUSmm27cODdneoXjputa6069qwuW6IIBIdLvdaF4OAcOiGeDc87zPs/vd57P96WpFq7p6enbGo1mjKZpeTabjU1MTCRagGnOZHFxcXxtbe1XKpUq7+zslJeXl//Mz8+Hy+Uy3RxSE9qTk5M3otFooVQqgef4Wl9f343FYoEmoISrxuNxFX5f9vb2jhn/PxUKhfLS0tIPfFifUESRUMV8Pv/M6XReRm5rTGQyGeXxeGxYe1ezeBpBOBx2rKysbO7v79d4Wy3Y2Nj4GQqFbgnhaugxwiuGJx99Pp9FLBbXxYTXvTqd7v3MzIy6riIWGxJnMpl7AwMD14xGYyMsSq1WUyQdUqn0eSPlusQIsbGrq+vl4OCgvhFQZd1utyv1en0gEolcqsi47nWJlUrlG5fLZVcoFFy2nDKSDpIWlUoVTCQSEk4lCHmJMZ2GTCbTiMVikfIZ88l7enoos9l8dXt7+z6fDicxSJUokqDX6xXcl2wCROoc0vQCWL3sNfLOSdzR0fHY4XC4tVotl40gmVwup9xuN4OQv+UyqCFGH9rg7SOGYVRcBs3IEG4J0nVnamrqOtvuBDGGgQg9+wHFcVEi4a0LNkbdd6TrPKo8ODc311mteIIYjT/a398/jK+s1jnVM0kXoufCFvq0GuiIGEVgQIhfoygM1QrteEa9dAL7ITiYCt4RMabOK5AyKKzKWtvupLcRciu8D5J0EuDDPyT/Snd39yh6VtY2NhYQSR9G79Ds7OxdskRjEyAufvb7/cPoO5Z6e1+xtVKrq6vfcFzyi/A3ZrPZ3GdNSlwgo5ekE4X2RIQGf2C1WlufFE0GBeGWYQ8YERWLxQtnUVB830MKLZfL9RHir8lkssCn2G751tZWEWe03zTKm15YWPiEiXXTYDB0Ig/t7yd8PRws4EicwWHxO4jHD8/C5HiTTqd1BwcHFozKU89origB+y/kmzgYpgOBQP4fGmUiZmJ+WNgAAAAASUVORK5CYII=')
}
.arrow {
float: right;
width: 12px;
height: 15px;
background-repeat: no-repeat;
background-size: contain;
background-position-y: bottom;
}
.number {
display: inline-block;
padding: 4px 10px;
margin: 0px 5px;
cursor: pointer;
}
.number:hover,
.number.active {
background-color: #3097D1;
border-color: #3097D1;
}
#paginatetexte {
padding-top: 6%;
}
#paginatebutton {
border-radius: 1px;
}
.tablecontainer {
margin-right: 2%;
margin-left: 2%;
}
.mailinbox {
margin-left: 1%;
}
</style>
<template>
<div class="panel panel-default tablecontainer">
<div class="panel-heading">ChartBookin Import</div>
<br>
<div class='col-md-12'>
<div class='col-md-3'></div>
<div class='col-md-3'>
<div class="panel panel-default">
<div class="panel-body">
<commitChart :width="150"
:height="150"></commitChart>
</div>
</div>
</div>
<div class='col-md-3'>
<div class="panel panel-default">
<div class="panel-body">
<InboxMessage :width="150"
:height="150"></InboxMessage>
</div>
</div>
</div>
</div>
<div class="panel-body">
<div class='col-md-3'>
<label> by Account </label>
<select v-model="teamId" class="form-control" rows="3">
<option VALUE="" disabled> CHOISIR UN TEAM</option>
<option v-for="option in options" v-bind:value="option.id">{{option.name}}</option>
</select>
</div>
<div class='col-md-3'>
<label> by Date</label>
<div class="form-group">
<input type="text" class="form-control" name="daterange"
value="01/01/2017-01/31/2017"/>
</div>
</div>
<div class='col-md-5'></div>
<div class='col-md-1'>
<label>Records</label>
<div class="form-group">
<select v-model="elementsPerPage" class="form-control">
<option v-for="option in filtre" v-bind:value="option">
{{ option }}
</option>
</select>
</div>
</div>
<div id="sixthTable">
<table class="table-bordered table-striped table-bordered table-hover">
<thead>
<tr>
<th v-for=" (key,value) in rows[0]" v-on:click="sortTable(value)">{{value}}
<div class="arrow" v-if="value == sortColumn"
v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-if=" rows.length > 0" v-for="row in rows">
<td v-for="(key, value) in row">{{ key }}</td>
<td>
<a :href="'/stat_booking_import/' + row.Id">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
</td>
</tr>
<tr v-else> No Result Founded</tr>
</tbody>
</table>
<div class="pagination">
<div id="paginatebutton">
«
<a class="" v-for="i in num_pages()"
v-bind:class="[i == currentPage ? 'active' : '']"
v-on:click="change_page(i)">{{i}}
</a>
»
</div>
<div class="col-md-12" id="paginatetexte">
<p v-if="pages > (elementsPerPage*currentPage) ">
Showing 1 to {{elementsPerPage * currentPage }} of {{ pages }} records
</p>
<p v-else>
Showing 1 to {{pages}} of {{ pages }} records
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import axios from 'axios';
import CommitChart from './Mail';
import InboxMessage from './Inbox';
export default {
components: {
CommitChart,
InboxMessage
},
data() {
return {
filtre: [10, 25, 50, 100],
option: 0,
options: [],
currentPage: 1,
elementsPerPage: 10,
pages: 0,
ascending: false,
sortColumn: '',
startdate: null,
enddate: null,
options: [],
teamId: null,
columns: [],
messages: [],
date: 0,
rows: {},
}
},
created() {
this.getData();
this.getTeams();
this.getMailInbox();
},
mounted() {
let vm = this;
$(document).ready(function () {
$('input[name="daterange"]').daterangepicker({
ranges: {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')]
},
locale: {
format: 'YYYY-MM-DD'
},
});
$('.applyBtn').click(function () {
vm.startdate = $("input[name=daterangepicker_start]").val();
vm.enddate = $("input[name=daterangepicker_end]").val();
vm.getData();
});
$('input[name="daterange"]').on('apply.daterangepicker', function (ev, picker) {
vm.startdate = $("input[name=daterangepicker_start]").val();
vm.enddate = $("input[name=daterangepicker_end]").val();
vm.getData();
});
});
},
watch: {
date: function () {
this.getData();
},
teamId: function () {
this.getData();
},
elementsPerPage: function () {
this.getData();
}
},
methods: {
getData() {
axios.get(`/admin/stat_booking_import.json/` + this.startdate + `/` + this.teamId + `/` + this.enddate + `/` + this.elementsPerPage + `?page=` + this.currentPage)
.then(response => {
this.rows = response.data.elements.data;
this.columns = Object.keys(this.rows[0]);
this.pages = response.data.elements.total_element;
})
.catch(e => {
this.errors.push(e)
})
},
getTeams() {
axios.get('/admin/team.json')
.then(response => {
this.options = response.data.data;
this.errors = [];
})
.catch(e => {
e.message = "aucun resultat trouvé essayer de choisir une autre date";
this.errors.push(e)
})
},
getMailInbox() {
axios.get(`/mailstorage.json`)
.then(response => {
this.messages = response.data.data;
console.log(this.messages);
})
.catch(e => {
this.errors.push(e)
});
},
sortTable(col) {
if (this.sortColumn === col) {
this.ascending = !this.ascending;
} else {
this.ascending = true;
this.sortColumn = col;
}
var ascending = this.ascending;
this.rows.sort(function (a, b) {
if (a[col] > b[col]) {
return ascending ? 1 : -1
} else if (a[col] < b[col]) {
return ascending ? -1 : 1
}
return 0;
})
},
num_pages() {
return Math.ceil(this.pages / this.elementsPerPage);
},
get_rows() {
var start = (this.currentPage - 1) * this.elementsPerPage;
var end = start + this.elementsPerPage;
return this.rows.slice(start, end);
},
change_page(page) {
this.currentPage = page;
this.getData();
}
},
}
</script>
}
#paginatebutton {
border-radius: 1px;
}
.tablecontainer {
margin-right: 2%;
margin-left: 2%;
}
.mailinbox {
margin-left: 1%;
}
</style>
You could try watch property https://v2.vuejs.org/v2/guide/computed.html#Watchers. From the doc:
While computed properties are more appropriate in most cases, there
are times when a custom watcher is necessary. That’s why Vue provides
a more generic way to react to data changes through the watch option.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.

Table Pagination with VueJS

I want to make pagination for my table but, when I run, my data does not appear anything. I am confused my code which is wrong perhaps here anyone can help me.
var newVue = new Vue({
el: '#app',
data: {
items: [
{ name: "item #1" }, { name: "item #2" }, { name: "item #3" }, { name: "item #4" },
{ name: "item #5" }, { name: "item #6" }, { name: "item #7" }, { name: "item #8" },
{ name: "item #9" }, { name: "item #10" }, { name: "item #11" }, { name: "item #12" }
],
start: 0,
limit: 2,
pagination: null,
total: 12
},
computed: {
filtered: function(){
return this.items.slice(0, this.limit)
}
},
mounted: function() {
this.limit = parseInt(this.pagination);
},
watch: {
pagination: function() {
this.limit = parseInt(this.pagination);
if(this.limit != this.start && this.start > 0)
this.start = parseInt(this.pagination);
this.limit = this.start + parseInt(this.pagination);
}
},
methods: {
paginate: function(direction) {
if(direction === 'next') {
this.start += parseInt(this.pagination);
this.limit += parseInt(this.pagination);
}
else if(direction === 'back') {
this.limit -= parseInt(this.pagination);
this.start -= parseInt(this.pagination);
}
},
},
filters: {
paginate: function(array, start, limit) {
return array.slice(start, limit);
}
}
});
.pager button {
background: #fff;
border: 1px solid #ddd;
border-radius: 15px;
color: #337AB7;
padding: 7px 13px;
text-align: center;
}
.pager button:hover {
background: #eee;
cursor: pointer;
outline: none;
}
.pager button:disabled {
background: #eee;
color: #bbb;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
<table class="table table-striped table-advance table-table-hover table-bordered">
<thead>
<th>Item</th>
</thead>
<tbody>
<tr v-for="item in filtered | paginate">
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-md-12 center">
<ul class="pager">
<li>
<button #click="paginate('previous')" :disabled="start <= 0">
Previous
</button>
</li>
<li>
<button #click="paginate('next')" :disabled="limit >= total">
Next
</button>
</li>
</ul>
</div>
</div>
</div>
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
There are few problems with your code, after removing those your code works as can be seen here in fiiddle.
You dont need | paginate with v-for as filtered is computed property which will give you paginated result
<tbody>
<tr v-for="item in filtered | paginate">
<td>{{ item.name }}</td>
</tr>
</tbody>
in filtered you needed to pass correct params in slice method.
computed: {
filtered: function(){
return this.items.slice(this.start, this.limit)
}
},`