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

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.

Related

Repeated data in vue

i`m having a problem with my vue, the problem is im trying to print 2 words, that is 'A.2' and 'B.3', but when im printing it, it just show 'B.3' and 'B.3'. here is my code
this is a simple quiz project, so everytime a user choose option a with true status it should be adding 1 point to the score, i haven`t made that yet.
<template>
<div class="hello">
<h1 v-if="show">hai</h1>
<h1 v-else>hehe</h1>
<p>{{ nama}}</p>
<input type="text" v-model="nama">
<button type="button" v-on:click="hideTitle">Click Me</button>
<h3> 1.Yang Dipakai Di sepatu adalah </h3>
<p>{{ nama}}</p>
<h3 v-for="j in jawaban">
<input type="radio">
{{j}}
</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data : function() {
return{
nama: 'Luthfi',
show: true
},
{
jawaban: 'A.2',
correct: true
},
{
jawaban: 'B.3',
correct: false
},
{
jawaban: 'C.4',
correct: false
}
},
methods: {
hideTitle() {
this.show = !this.show
}
},
mounted: function () {
this.nama = 'Fitra'
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
i expect there is 4 output from option A to D, but it kept showing me same option
In your code, data() returns only one object that contains
{
nama: 'Luthfi',
show: true
}
You must change this like:
data : function() {
return{
nama: 'Luthfi',
show: true,
jawaban: 'A.22',
correct: true,
jawabann: 'B.3'
}
}

Can I execute a method when an input box gets to a certain length?

I have an input box that takes a string. Can I execute a method (in vue.js) when the length of the string gets to a certain number?
something like
<input v-if="inputBox.length == 6 THEN runme()"...>
You can use watch option, you'll be able to react to data changes :
new Vue({
el: '#root',
data: {
message: '',
inputLength : undefined
},
methods : {
doSomething(){
console.log('I did it !')
}
},
watch :{
message : function(val) {
if(val.length>=5){
this.inputLength = val.length
this.doSomething();
}
}
}
})
.container {
padding-top: 2em;
}
.intro {
font-size: 1.5em;
margin-bottom: 1.5em;
}
.input-value {
margin-top: 1em;
font-size: 1.25em;
}
.highlight {
color: #00d1b2;
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container">
<h1 class="intro">Binding with Vue</h1>
<div id='root' class="box">
<label class="label">Enter text here</label>
<input class="input is-medium" type='text' id='input' v-model='message'>
<p class="input-value">The value of the input is: <span class="highlight">{{ inputLength }}</span></p>
</div>
</div>
In this example, if input length is >= 5 then it will change the inputLenght value in data and execute a method.
For more informations about this, go see :
https://v2.vuejs.org/v2/guide/computed.html#Watchers
You can use a watcher to trigger a method when the string exceeds the length:
new Vue({
data () {
return {
model: ''
}
},
watch: {
model: {
handler: function (value) {
if (value.length >= 6) {
this.trigger()
}
}
}
},
el: '#app',
methods: {
trigger () {
alert('hi there')
}
},
template: `<input v-model="model">`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

How to make the props binding reactive when using 'createElement' function

Vue.config.devtools = false
Vue.config.productionTip = false
let modal = Vue.extend({
template: `
<div class="modal">
<p>Balance: {{ balance }}</p>
<input #input="$emit('input', $event.target.value)" :value="value">
<button #click="$emit('input', balance)">ALL</button>
</div>
`,
props: ['balance', 'value']
})
function makeComponent(data) {
return { render(h) { return h(modal, data) } }
}
Vue.component('app', {
template: `
<div>
<p>Balance: {{ balance }}</p>
<p>To withdraw: {{ withdrawAmount }}</p>
<p>Will remain: {{ balance - withdrawAmount }}</p>
<button #click="onClick">Withdraw</button>
<modal-container ref="container"/>
</div>`,
data () {
return {
withdrawAmount: 0,
balance: 123
}
},
methods: {
onClick () {
this.$refs.container.show(makeComponent({
props: {
balance: String(this.balance),
value: String(this.withdrawAmount)
},
on: {
input: (value) => {
this.withdrawAmount = Number(value)
}
}
}))
}
}
})
Vue.component('modal-container', {
template: `
<div>
<component v-if="canShow" :is="modal"/>
</div>
`,
data () {
return { modal: undefined, canShow: false }
},
methods: {
show (modal) {
this.modal = modal
this.canShow = true
},
hide () {
this.canShow = false
this.modal = undefined
}
}
})
new Vue({
el: '#app'
})
.modal {
background-color: gray;
width: 300px;
height: 100px;
margin: 10px;
padding: 10px;
}
* {
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
color: #2c3e50;
line-height: 25px;
font-size: 14px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17-beta.0/vue.js"></script>
<div id="app">
<app></app>
</div>
This is a simplified version of our application using vue-js-modal. The basic Idea is that we pass a component declaration along with required VNodeData to the plugin function and it would append that component to a pre-defined DOM point with proper data bindings.
And our usecase is that:
User clicks 'Withdraw'
Modal pops up with an input field and static field displaying user's balance
User enters the amount he/she wants to withdraw and that amount and resulting balance are displayed in the callee component.
There is a 'ALL' button besides the input field for user to easily enter a number equal to his/her balance
Problem: When user clicks 'ALL', the 'modal' component fires an 'input' event with value of the balance and the callee component receives that event and updates its 'withdrawAmount'. But the 'withdrawAmount' is supposed to be passed back to the 'modal' as 'value' prop and further updates the input field, which doesn't happen.

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/

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.