how can I update progress bar when json data change using vue.js without refresh - vue.js

I try to make a boostrap dashboard page for tracing status of my python aplication running on server.
On server side, python app update data.json file when reach certain status.
On client side, vue.js handle content creation.
I have a problem when I try to update progress bar, because i need to refresh page so that progress appears.
Any suggestion how can I make live progress bar in my view without refresh?
index.html
<div class="item" v-for="item in order">>
<div class="progress">
<div class="progress-bar bg-warning" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" :style="{ width: item.completion + '%' }">
</div>
</div>
</div>
app.js
window.addEventListener('load', () => {
window.vue = new Vue({
el: '#app',
name: 'Order',
data: {
isLoading: true,
order: [],
},
created() {
fetch('./data.json')
.then((res) => { return res.json() })
.then((res) => {
this.isLoading = false;
this.order = res.order;
})
}
})
});
data.json
{
"order": [
{
"customer": "Mr. Smith",
"price": "60",
"status": "Pending",
"orders": "Something",
"completion": 40,
"isAvailable": true,
"isEligible": true
}
]
}
edit: I solve my issue with adding watcher to app.js
watch: {
order() {
this.updateorder();
}
},
methods: {
updateorder() {
fetch('./data.json?_timestamp=' + Date.now())
.then((res) => { return res.json() })
.then((res) => {
this.order = res.order;
})

Does it help?
let i = 0;
const emulateRequest = () => Promise.resolve({
"order": [
{
"customer": "Mr. Smith",
"price": "60",
"status": "Pending",
"orders": "Something",
"completion": i++,
"isAvailable": true,
"isEligible": true
}
]
});
new Vue({
el: '#app',
data: () => ({
isLoading: true,
order: [],
}),
created() {
this.load();
},
methods: {
load() {
//fetch('./data.json')
// .then((res) => { return res.json() })
emulateRequest()
.then((res) => {
this.isLoading = false;
this.order = res.order;
if (this.order.some(({ completion }) => completion !== 100)) {
setTimeout(() => {
this.load();
}, 1000);
}
})
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div id="app">
<div class="item" v-for="item in order">
<div class="progress">
<div class="progress-bar bg-warning" role="progressbar" :aria-valuenow="item.completion" aria-valuemin="0" aria-valuemax="100" :style="{ width: `${item.completion}%` }">
</div>
</div>
</div>
</div>

Related

Pass API into an array

I am trying to pass the data from methods into my empty array rows ,how i can easly pass it or how to call the API into rows
export default {
data () {
return {
data: {
headers: [
{ title: 'id', key: 'id' },
{ title: 'name', key: 'name' },
{ title: 'city', key: 'city' },
{ title: 'address', key: 'address' }
],
rows: []
}
}
},
components: {
BaseTable
},
methods: {
fetchUsers () {
this.$axios.get('https://605c40b36d85de00170d9a8f.mockapi.io/user/zurich')
.then(({ data }) => {
this.tableData = data
console.log(data)
})
}
}
}
When the response from your api arrives, you just have to add to your property declared in the method data.
new Vue({
el: "#app",
data() {
return {
row: []
}
},
methods: {
fetching() {
fetch('https://rickandmortyapi.com/api/character')
.then(res => res.json())
.then(res => {
console.log(res);
this.row = res.results; //Results added into array
})
.catch(err => console.error(err))
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="fetching">Fetch Api</button>
<div v-for="(item, key) in row" :key="key" style="margin: 2rem auto;" >
<p>- {{ item.id }} | {{ item.name }} | {{ item.status }}</p>
</div>
</div>

Why this $emit event is not working between component and parent in Vue.js?

Playing with an API and building a simple dropdown selector in Vue, I am stuck as of why events of a component do not reach the parent. I have looked on the documentation, with no success. I have tried both this.$emit and this.$parent.$emit with no success
Code sample here:
https://jsfiddle.net/behyfnxw/
The point is that method "updated" in the parent is never called when selecting a university from the dropdown menu.
Pretty new to Vue and frontend, any feedback is more than welcome. Thanks!
<!DOCTYPE html>
<div id="app">
<div class="row">
<div class="col-lg-6 offset-lg-3">
<entity-selector :items="univs" :value="u0"></entity-selector>
<entity-selector :items="deps" :value="d0"></entity-selector>
<entity-selector :items="courses" :value="c0"></entity-selector>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
Vue.component('entity-selector', {
props: ['items', 'value'],
data: function () {
return {
selected: "u0",
}
},
created: function () {
console.log("entity selector created just now!");
},
mounted: function () {
console.log("mounted");
this.selected = this.value;
},
watch: {
selected: function (newValue) {
console.log("watch!", newValue);
this.selected = newValue;
console.log("to send event");
this.$emit("updated");
console.log("sent event");
}
},
methods: {
updated: function () {
console.log("selected item:", this.selected);
this.$emit("updated", "univ", this.selected);
console.log("selected item:", this.selected);
},
},
template:`
<p class="col-xs-12">
<select class="form-control" #click="updated" v-model="selected" v-cloak>
<option v-for="item in items" :value="item.id">{{item.fullname}}</option>
</select>
</p>
`
});</script>
<script>
var vm = new Vue({
el: '#app',
data: {
u0: "u0",
d0: "d0",
c0: "c0",
default_univ: {"id": "u0", "fullname": 'choose univ', "total": 0},
default_dep: {"id": "d0", "fullname": 'choose dep', "total": 0},
default_course: {"id": "c0", "fullname": 'choose course', "total": 0},
univs: [{"id": "u0", "fullname": 'choose univ', "total": 0}],
deps: [{"id": "d0", "fullname": 'choose dep', "total": 0}],
courses: [{"id": "c0", "fullname": 'choose course', "total": 0}],
},
created: function () {
console.log("main vue created just now!");
this.fetch_entities("gr", 1);
},
methods: {
fetch_entities: function (parent, filetypeid) {
var main_endpoint = "https://api.trelosfoititis.gr/v1";
var url = main_endpoint + "/list?parentid=" + parent + "&filetypeid=" + filetypeid;
var self = this;
axios.get(url)
.then(function (response) {
self.fetched(parent, filetypeid, response.data.response.data);
});
},
updated: function (entity_type, entity_id){
console.log("updated!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", entity_type, entity_id);
},
fetched: function (parent, filetypeid, results){
console.log("fetched", parent, filetypeid);
if(parent == "gr"){
this.univs = (results.length > 0) ? [].concat([this.default_univ], results) : [this.default_univ];
}
},
}
})
</script>
</html>

Adding class to p tags throws eslint error

I am working on a project and integrating Vue within it. There's eslint and vues eslint included with the webpack compiler. When I try to compile the code, I get an error cannot read property 'replace' of undefined. This appears whenever I try to add a <p> tag with any sort of attribute to it. Looking at the code below <p class="name">John Smith</p> will throw the error. If I remove it, the error's gone.
I'm not using replace anywhere within the file, so I'm not sure why eslint would be throwing this error, or how to get past it.
Component file
<template>
<div v-if="committee_id > 0">
<a
href="#"
class="line-btn blue"
><span>Return to the section's committees list</span></a>
<h2>{{ data.title }}</h2>
<div
v-html="data.content"
></div>
<div class="btn-cnt">
<a
href="#"
class="curve-btn blue"
#click.prevent="goBack"
><span>Join the committee</span></a>
</div>
<div class="roster detail-roster">
<h3>Committees Roster</h3>
<div class="row">
<div class="col-xs-6">
<p class="name">John Smith</p>
</div>
</div>
</div>
</div>
</template>
<script>
import {EventBus} from '../event-bus';
import axios from 'axios';
export default {
data() {
return {
classArray: {
name: 'name',
title: 'title',
},
committee_id: 0,
data: [],
api_url: (API.restUrl !== undefined) ? API.restUrl : '',
};
},
created() {
EventBus.$on('committee-post-id', committee_id => {
this.committee_id = committee_id;
this.getCommitteePost();
});
},
methods: {
getCommitteePost: () => {
this.$nextTick(function () {
axios.get(`${this.api_url + this.committee_id}`)
.then(({data}) => {
if (data.message === 'success') {
this.$nextTick(function () {
this.data = data;
});
}
})
.catch(err => {});
});
},
goBack: () => {
}
},
name: 'section-committee'
};
</script>
Eslint
module.exports = {
"root": null,
"globals": {
"wp": true
},
"env": {
"node": true,
"es6": true,
"amd": true,
"browser": true,
"jquery": true
},
'extends': [
'plugin:vue/recommended'
],
"parserOptions": {
"ecmaFeatures": {
"globalReturn": true,
"generators": false,
"objectLiteralDuplicateProperties": false,
"experimentalObjectRestSpread": true
},
"ecmaVersion": 2017,
"sourceType": "module"
},
"plugins": [
"import"
],
"settings": {
"import/core-modules": [],
"import/ignore": [
"node_modules",
"\\.(coffee|scss|css|less|hbs|svg|json)$"
]
},
"rules": {
"no-console": 0
}
}
Unsure still what's going on, but a resolution for the linter was to change the following in my linter file.
'extends': [
'plugin:vue/base'
],

Getting bootstrap vue pagination to play with REST api

Trying to get Bootstrap Vue to play with a REST api that returns data for one page and the total number of records (based on this):
<template>
</div>
<b-pagination
v-on:change="onPageChange"
:total-rows="totalRows"
:per-page="perPage"
v-model="currentPage" />
<b-table responsive striped hover show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
#filtered="onFiltered">
</b-table>
</div>
</template>
<script>
...
export default {
name: 'TableList',
data() {
return {
module: null,
title: 'Table',
items: [],
fields: [],
errors: [],
currentPage: 1,
perPage: 15,
totalRows: 0,
pageOptions: [ 5, 10, 15 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null,
}
},
created() {
...
this.fetch();
},
methods: {
fetch() {
var me = this;
var requestParams = {
page: this.currentPage,
per_page: this.perPage
};
if(this.sortBy) {
requestParams = Object.assign({ sort_by: this.sortBy }, requestParams);
}
Rest('GET', '/table/' + this.table, requestParams, this.$root.user.token)
.then(response => {
me.items = response.data[1]
me.totalRows = response.data[0].total_entries
})
.catch(error => {
this.errors.push('Error: ' + error.response.status + ': ' + error.response.statusText)
})
.finally(() => {
//alert('turn off loader!');
});
}
}
</script>
This Vue works if I fetch the entire table. However, when I use the REST api to return one page at a time, the number of pages is calculated to be 1, and the forward and end links are inactive. Thus, I am unable to trigger a request for e.g. page 2.
The REST api correctly returns the total number of rows in the table, and the number of rows requested, but Bootstrap Vue does not appear to be watching/reacting to changes to this.totalRows.
What have I missed?
You need to set the per-page prop to 0 on the b-table component to disable the local pagination and allow b-pagination to handle the data. Here's an example:
new Vue({
el: '#app',
data() {
return {
items: [],
fields: [{
key: 'postId',
label: 'Post ID'
},
{
key: 'id',
label: 'ID'
},
{
key: 'name',
label: 'Name'
},
{
key: 'email',
label: 'Email'
},
{
key: 'body',
label: 'Body'
}
],
currentPage: 0,
perPage: 10,
totalItems: 0
}
},
mounted() {
this.fetchData().catch(error => {
console.error(error)
})
},
methods: {
async fetchData() {
this.items = await fetch(`https://jsonplaceholder.typicode.com/comments?_page=${this.currentPage}&_limit=${this.perPage}`)
.then(res => {
this.totalItems = parseInt(res.headers.get('x-total-count'), 10)
return res.json()
})
.then(items => items)
}
},
watch: {
currentPage: {
handler: function(value) {
this.fetchData().catch(error => {
console.error(error)
})
}
}
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.js"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-table show-empty :items="items" :fields="fields" :current-page="currentPage" :per-page="0"></b-table>
<b-pagination size="md" :total-rows="totalItems" v-model="currentPage" :per-page="perPage"></b-pagination>
</div>
You can also disabled local pagination in the table, so that your items provider becomes responsible for controlling the pagination.

vee validate on custom select2 component not works

I try to use vee-validate on a custom component where if nothing selected on submit should show the validation error
the template looks as it follows
<div id="validate" class="container">
<form #submit.prevent="store()" data-vv-scope="time-off" autocomplete="off">
<div class="col-lg-6">
<div class="form-group">
<select2
:options="options"
placeholder='Select...'
v-validate="'required|in:sick, vacation'"
v-model="form.category"
id="categorywidget"
class="form-control">
</select2>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary pull-right">
Send Request
</button>
</div>
</div>
</div>
</form>
</div>
and here is my vue code
Vue.component('Select2', {
props: ['options', 'value', 'id', 'placeholder', 'clear'],
template: '<select><slot></slot></select>',
mounted: function () {
var vm = this
$(this.$el)
.select2({
data: this.options,
placeholder: this.placeholder,
theme: "bootstrap",
width: '100% !important',
dropdownAutoWidth : true,
allowClear: this.clear !== '' ? this.clear : false
})
.val(this.value)
.attr("id", this.id !== null ? this.id : Date.now() + Math.random() )
.trigger('change')
// emit event on change.
.on('change', function () {
vm.$emit('input', $(this).val())
})
},
watch: {
value: function (value) {
// update value
$(this.$el).val(value).trigger('change');
},
options: function (options) {
// update options
$(this.$el).select2({data: options})
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
});
Vue.use(VeeValidate)
new Vue({
el: '#validate',
data: {
form: {
scopes: [],
category: 'null',
daterange: '',
note: ''
},
options: [
{text: 'Vacation', id: 'vacation'},
{text: 'Sick', id: 'sick'},
{text: 'Not ok', id: 'not-ok'},
]
},
methods: {
store: function() {
this.$validator
.validateAll()
.then(function(response) {
alert('is ok!')
// Validation success if response === true
})
.catch(function(e) {
// Catch errors
alert('not ok!')
})
}
}
});
here is a pen what I created
https://codepen.io/anon/pen/gXwoQX?editors=1111
on submit null the validation is passes. what is wrong with this codes
There are issues raised on this subject in github - #590, #592.
None of them lead me to a solution, so I'd suggest a check inside the promise
.then(response => {
if(this.errors.items.length === 0) {
alert('is valid')
} else {
alert('is not valid')
}
A couple of other notes:
See below, catch() is technically the wrong place to handle validation errors, that should be inside the then(response) and when invalid response === false (but not working because of bug).
The catch() is probably for 'I blew up' errors.
.catch(function(e) {
// Catch errors
// alert('not valid') // don't process these here
})
Also this
v-validate="'required|in:sick, vacation'"
should be this (remove space before vacation)
v-validate="'required|in:sick,vacation'"