Vue data referencing issue - vue.js

This is strange because it was just working last night, but basically I have a Vue app that's pulling JSON from my backend. Code below. The strange part is that while the loadData function is running and I see the 'Loaded Data' message in console along with the list of items from the JSON, I then get a console error saying 'items is not defined'. I must have made a subtle typo or some dumb change but I can't find it anywhere!! Any ideas?
HTML snippet:
<div id="app">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Query</th>
<th>Initiated By</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<div v-for="item in items">
<td>{{ item.id }}</td>
<td>{{ item.query }}</td>
<td>{{ item.user }}</td>
<td>{{ item.type }}</td>
</div>
</tr>
</tbody>
</table>
</div>
</div>
(And then <script src="app.js"></script> right before </body>)
JS code:
new Vue({
el: '#app',
data: {
items: [],
interval: null
},
methods: {
loadData: function () {
$.get('http://localhost:4567/getQueue', function (response) {
this.items = response.results;
console.log("Loaded data.")
console.log(response.results)
}.bind(this));
}
},
created: function () {
console.log("Loading data...")
this.loadData();
console.log(items)
this.interval = setInterval(function () {
this.loadData();
}.bind(this), 3000);
},
beforeDestroy: function(){
clearInterval(this.interval);
}
});

You are getting the error
items is not defined
because of following line:
created: function () {
console.log("Loading data...")
this.loadData();
console.log(items) <== this should be console.log(this.items)

Turns out there was a few issues in my code.
1) As was pointed out by Saurabh, I forgot to put this.items instead of items.
2) this can't be referenced inside of the function I defined as I have it... instead, the function has to be defined with =>, for example:
$.get('http://localhost:4567/getQueue').then((response) => {
this.items = response.data.results;
console.log("loadData finished - items length is: "+this.items.length)
})
3) The big error I had was that my div bind with items was inside the table tag, which apparently isn't okay to do. Instead I applied the Vue binds to the existing tags (table, tr).

Related

Vue.js Displaying data from websocket into a table and sending data to other component

Version: Vue CLI 2.6.x
I am trying to resolve two issues:
Issue 1:
My Vue app has subscribed to updates via a websocket. I am getting the data continuously and need to keep the table updated with the received data. However the table remains empty even when the list (aqiDataList) has content in it.
Issue 2:
I also need to pass the aqiDataList to the AQITableComponent (where the actual table was originally suppose to be) but having this same issue
App.vue
<template>
<v-container>
<AQITableComponent :aqiList="aqiDataList" />
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">
Name
</th>
<th class="text-left">
Age
</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in aqiDataList"
:key="item.id"
>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>{{ item.location }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-container>
</template>
<script>
import AQITableComponent from './AQITableComponent';
export default {
name: 'AQIComponent',
components: {
AQITableComponent
},
data: function () {
return {
connection: null,
aqiDataList: []
};
},
mounted() {
},
methods: {
},
created: function () {
console.log("Starting connection to WebSocket Server");
this.connection = new WebSocket("wss://my-websocket.com");
this.connection.onmessage = function (event) {
//console.log(event.data);
let aqiDataString = event.data;
this.aqiDataList = [];
let parsedData = JSON.parse(aqiDataString);
parsedData.forEach((aqiData) => {
this.aqiDataList.push({
... object
});
});
console.log(this.aqiDataList);
};
this.connection.onopen = function (event) {
console.log(event);
console.log("Successfully connected to the echo websocket server...");
};
},
}
</script>
AQITableComponent.vue
<template>
<v-container>
<v-simple-table>
<template v-slot:default>
.. same table as shown above
</template>
</v-simple-table>
</v-container>
</template>
<script>
export default {
name: 'AQITableComponent',
props: ['aqiList'],
data: function () {
},
}
</script>
(1) Try using the arrow function for the onmessage event:
from: this.connection.onmessage = function (event) {...}
to: this.connection.onmessage = (event) => {...}
or: this.connection.addEventListener("message", (event) => {...});
This way the this.aqiDataList will be available on your component. Inside the event callback.
This should also solve the problem (2) since your array is not being updated on the first place.

Load More Data On Scroll With Vue And Vuex

I would like to ask how can I display more data by using Vue and vuex. all data stored in vuex-store management already. From State management now I want to load more data on scrolling.
I found online solution by ajax. but I need to loading form state management (Vuex).
This is my Vue template:
<template>
<div>
<div class="panel panel-default">
<div class="panel-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<tr>
<th>Name - Number of Products: <span style="color: red"> {{products}} </span></th>
<th width="100"> </th>
</tr>
</tr>
</thead>
<tbody v-if="isLoaded">
<tr v-for="company, index in companies">
<td>{{ company.name }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return { }
},
computed: {
companies(){
return this.$store.getters['exa1Company/getProducts'];
},
products(){
return this.$store.getters['exa1Company/countProducts'];
}
},
mounted() {
this.$store.dispatch('exa1Company/indexResource');
}
}
</script>
My vuex store file is partial for simplicity
export const getters = {
countProducts(state) {
return state.list.data.length;
},
getProducts(state) {
return state.list.data;
},
getTodoById: (state) => (id) => {
return state.list.data.find(tod => tod.id === id)
}
};
export default {
namespaced: true,
state: customerState,
getters,
actions,
mutations,
};
something like this should work. use companiesLoaded in the template, and increase page when scrolled to bottom. I hope this helps.
data: function () {
return {
page: 1,
perPage: 20
}
},
computed: {
companies(){
return this.$store.getters['exa1Company/getProducts'];
},
companiesLoaded(){
return this.companies.slice(0, this.page * this.perPage)
},
...

Calling a method into another method in vue

I'm trying to call a method from inside another method in vue.
What I get is an undefined in my console, but what I really want is the id that is called in the getId function
In a whole what I'm tring to do is use the addEvent function to get the checkbox events so that I can get a true or false from it and then send that to the saveCheckbox function and from the saveCheckbox function call the getId function to get the ID of that specific checkbox.
I hope I was able to explain it properly. If it's still unclear please let me know.
This is what I have
<template>
<div class="card-body">
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">Active</th>
<th scope="col">Title</th>
</tr>
</thead>
<tbody>
<tr v-for="(category, index) in categories" >
<td>
<input name="active" type="checkbox" v-model="category.active" #change="getId(category.id)" #click="addEvent">
</td>
<td>
{{ category.title }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: [
'attributes'
],
data(){
return {
categories: this.attributes,
}
},
methods: {
getId(id){
console.log(id);
return id
},
saveCheckbox(event){
console.log(this.getId());
},
addEvent ({ type, target }) {
const event = {
type,
isCheckbox: target.type === 'checkbox',
target: {
value: target.value,
checked: target.checked
}
}
this.saveCheckbox(event.target.checked)
}
},
mounted() {
console.log('Component mounted.')
}
}
</script>
You have to pass argument (Id) to getId method
Having a sort overview, you are not passing any Id to the method, and it trys to return that id. so maybe, that is what is not defined ?
The method calling is done well. with the this. keyword before it

Vuejs and datatables: table empty when using v-for to fill data

I'm trying to fill a datatable using vuejs v-for directive and ajax to get the data but the table is always showing "No data available in table" even though there are some data shown and also in the bottom says "Showing 0 to 0 of 0 entries". I guess this is because vuejs is reactive and the table can't recognize the changes maybe?
I've been searching and trying for a while but with no solution found..
thanks a lot! :)
here's the template:
<table id="suppliersTable" class="table table-hover table-nomargin table-bordered dataTable">
<thead>
<tr>
<th>...</th>
...
</tr>
</thead>
<tbody>
<tr v-for="supplier in suppliers">
<td>{{ supplier.Supplier_ID }}</td>
<td>...</td>
...
</tr>
</tbody>
</table>
and the vue and ajax:
<script>
export default {
data() {
return {
suppliers: [],
}
},
methods: {
fetchSuppliers() {
this.$http.get('http://localhost/curemodules/public/suppliers/list')
.then(response => {
this.suppliers = JSON.parse(response.bodyText).data;
});
}
},
created() {
this.fetchSuppliers();
},
}
Once initialized, DataTables does not automatically reparse the DOM. Here's a relevant FAQ:
Q. I append a row to the table using DOM/jQuery, but it is removed on redraw.
A. The issue here is that DataTables doesn't know about your manipulation of the DOM structure - i.e. it doesn't know that you've added a new row, and when it does a redraw it will remove the unknown row. To add, edit or delete information from a DataTable you must use the DataTables API (specifically the row.add(), row().data() and row().remove() methods to add, edit and delete rows.
However, you can call table.destroy() to destroy the current instance before reinitializing it. The key is to delay the reinitialization until $nextTick() so that Vue can flush the DOM of the old DataTables. This is best done from a watcher on suppliers so that the DataTables reinitialization is done automatically when the variable is updated in fetchSuppliers().
mounted() {
this.dt = $(this.$refs.suppliersTable).DataTable();
this.fetchSuppliers();
},
watch: {
suppliers(val) {
this.dt.destroy();
this.$nextTick(() => {
this.dt = $(this.$refs.suppliersTable).DataTable()
});
}
},
demo
I know this is a bit late answer but I just encountered this problem just today and my only solution for this issue is using setTimeout function.After fetching data using axios I set a bit of delay then init the data-table. With this work around v-for works fine.
See below for my code.
GetDepartmentList(){
axios.get('department')
.then((response) => {
this.departmentList = response.data;
// this.dataTable.rows.add(response.data).draw();
setTimeout(() => $('#department-data-table').DataTable(), 1000);
})
.catch((error) => {
if (error.response.status == 401) {
alert('User session has expired. Please login again.');
location.replace("/login");
}
});
},
Also you can use .rows.add() function if you want to draw row data in the table without using v-for of vue. Refer to this doc.
You can using Axios in Vuejs, you try see the following above:
<template>
<div class="danhsach">
<h2>{{title}}</h2>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Password</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr v-for='data in datas'>
<td>{{data.id}}</td>
<td>{{data.name}}</td>
<td>{{data.password}}</td>
<td>{{data.age}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default{
data(){
return {
title:"Tile Lists",
datas:[]
}
},
created:function(){
this.danhsach_user();
},
methods:{
danhsach_user(){
this.axios.get('https://599f807effe73c0011b9fcc5.mockapi.io/api/user').then((response)=>{
this.datas=response.data;
});
}
}
}
</script>

vuejs2 how to get v-for items to display at specific intervals?

Say I have a group of cars and I want to display each row...3 seconds at a time. How can I do this in Vuejs2?
<tbody>
<tr v-for="(car) in cars">
<td><img v-bind:src="car.photo" width="40px" height="40px" alt=""></td>
<td><router-link :to="{path:'/car/' + car.id}" >{{ car.name }}</router-link></td>
<td>{{ car.make }}</td>
<td></td>
<td>{{ car.created }}</td>
</tr>
</tbody>
something like this.
stored what to show currently in currentCarIndex.
use setInterval to change currentCarIndex every 3 seconds
btw, v-for and v-if shouldn't be used together, so I add a <template> tag as an empty wrapper to execute v-for
<template>
<tbody>
<template v-for="(car,i) in cars">
<tr :key="i" v-if="i<=currentCarIndex">
<td><img v-bind:src="car.photo" width="40px" height="40px" alt=""></td>
<td>
<router-link :to="{path:'/car/' + car.id}">{{ car.name }}</router-link>
</td>
<td>{{ car.make }}</td>
<td></td>
<td>{{ car.created }}</td>
</tr>
</template>
</tbody>
</template>
<script>
export default {
data() {
return {
currentCarIndex: 0,
cars: "..."
};
},
mounted() {
const interval = setInterval(() => {
if (this.currentCarIndex + 1 < this.cars.length) this.currentCarIndex++;
else clearInterval(interval);
}, 3000);
}
};
</script>
I was having this exact problem a couple of hours ago on an app I'm working on. I have a list of reviews and I wanted the reviews to display at interval so that it looks like the list is 'filled in' top down so that I can create a cascading effect. Something like this:
The documentations points out that you can use transition-group but personally I wasn't able to get them working for me so what I did is I created a wrapper component with a delay property on it and I passed in the time the component should wait before rendering. I did this using a simple v-if in the component's template.
What you could do is add a show-in and visible-for prop to a wrapper component like this:
<flashing-row v-for="(car, i) in cars" :show-in="i * 3000" :visible-for="2900">
// Stuff inside my row here....
</flashing-row>
and then define flashing-row like this:
Vue.component('flashing-row', {
props: {
showIn: {
type: Number,
required: true,
},
visibleFor: {
type: Number,
required: true,
},
},
data() {
return {
isVisible: false,
};
},
created() {
setTimeout(() => {
// Make component visible
this.isVisible = true;
// Create timer to hide component after 'visibleFor' milliseconds
setTimeout(() => this.isVisible = false, this.visibleFor);
}, this.showIn);
},
template: '<tr v-if="isVisible"><slot></slot></tr>'
});
You can see an example of the code in JSFiddle. This approach is especially good because:
You don't repeat yourself if you're going to be doing this at more than one place.
Makes your code more maintainable and easier to browse, read, and thus understand and modify later on.
And of course you can play around with the props and expand on it depending on what you need. The possibilities are really endless.