repetedly call a axios.get to display data from endpoint - vue.js

I working on backend written in flask that returns REST API for various task, one of which is to request deployment,
Then log gets filled in the database. So I have created another REST API endpoint to return log message for the specific request id. Now what I want is to once the showStatus is true the log_url used by axios.get should be constantly hitting and retrieving data from the backend endpoint retrieving data after 30 seconds. and show it in the show below as statusMessage.
<script>
import Vue from 'vue';
import axios from "axios";
import Multiselect from "vue-multiselect";
Vue.component("multiselect", Multiselect);
export default {
name: 'SelectServices',
data: function() {
return {
selectedService: "",
services: [],
updateExisting: "",
showStatus: false,
statusMessage : ""
}
},
mounted() {
console.log("running script");
},
methods : {
selectServiceToDeploy: function(){
// alert("micro services");
},
deploySelected: function(){
this.showStatus = true ;
// animate open the status window.
$("#status_update").animate({height: '500'})
var url = "http://localwebsite.com:5060/services/request_deploy";
axios.post(url)
.then(response => {
if (typeof response.data.reason != "undefined"){
alert("Recieved Status: " + response.data.status + ",\nReason: " + response.data.reason);
}
var req_id = response.data.result.request_id;
this.statusMessage = "Initiating deployment of Services for Request ID: " + req_id ;
var log_url = "http://localwebsite.com:5060/services/get_logs/" + req_id;
axios.get(log_url)
.then(response => {
if (response.data.status == "success"){
this.statusMessage = this.statusMessage + response.data.logs;
}
})
})
.catch((err) => {
console.log("Error happened: " + err.request.message);
alert(err);
return Promise.reject(err);
})
console.log(url);
console.log(log_url);
}
}
}
based on the above logic I have written in vue it successfully request for deployment but the logs don't get printed in the UI! how can I constantly retrieve data from http://localwebsite.com:5060/services/get_logs/" + req_id after every 30 seconds.

You can do something in the mounted hook:
mounted(){
this.interval = setInterval(() => {
axios.get(this.my_url).then(res => { /* do something */});
}, 30000 );
},
data(){
return {
interval: undefined,
my_url: undefined
}
}
setInterval is a javascript function that allow you to run a function every predefined milliseconds. You can cancel it later with the following command: clearInterval(this.interval). You can also change my_url when you want :)

Related

Nuxt 3 - Server not ready on mount, Volar engine confused

I have two issues that may or may not be related.
Overview
Folder Structure
pages
|---user.vue
server
|---api
|---profile.get.ts
|---profile.post.ts
The tech is Nuxt3 using the server and Supabase.
profile.get.ts
import { serverSupabaseClient, serverSupabaseUser } from "#supabase/server"
import { Database } from "~~/types/supabase"
export default defineEventHandler(async (event) => {
try {
const supabase = serverSupabaseClient<Database>(event)
const user = await serverSupabaseUser(event)
const query = getQuery(event)
const { data, error } = await supabase.from('profiles').select('*').eq('email', query.email).single()
if (error) throw { status: error.code, message: error.message }
return { displayName: data.display_name, avatarUrl: data.avatar_url }
} catch (err) {
console.error('Handled Error:', err)
}
})
profile.post.ts
import { serverSupabaseClient } from "#supabase/server"
import { Database } from "~~/types/supabase"
export default defineEventHandler(async (event) => {
const supabase = serverSupabaseClient<Database>(event)
const { displayName, avatarUrl, email }: { displayName: string, avatarUrl: string, email: string } = await readBody(event)
const { error } = await supabase.from('profiles').update({ display_name: displayName, avatar_url: avatarUrl }).match({ email })
if (error) throw new Error(error.message)
return { status: 200 }
})
user.vue Snippet
onMounted(() => {
setTimeout(() => {
getProfile()
}, 100) // Fails when around 50 or less
})
async function getProfile() {
const { data, error } = await useFetch('/api/profile', { method: 'GET', params: { email: user.value?.email } })
console.log(data.value)
console.log(error.value)
displayName.value = data.value!.displayName || ''
avatarUrl.value = data.value!.avatarUrl || ''
}
Problem 1
When user.vue mounts, I want to call my Nuxt API (profile.get.ts) and fetch user data (display name, avatar url) from the Supabase database. However, I receive this error when fetching on mount: FetchError: 404 Cannot find any route matching /api/profile. (/api/profile). However, if I use setTimeout to 100ms, it fetches fine. That makes me think the API server is simply not ready, but the documentation doesn't mention that and encourages fetching during lifecycle.
Problem 2
Volar seems to be confused about the typing of data from getProfile().
Property 'displayName' does not exist on type '{ status: number; } | { displayName: string | null; avatarUrl: string | null; }'.
Property 'displayName' does not exist on type '{ status: number; }'.ts(2339)
However, this is the typing from profile.post.ts even though I'm using profile.get.ts.
Current Behavior
Without setTimeout at 100ms or greater, it will fail with the 404 message
With setTimeout at 100ms or greater, or with getProfile() called from a button, there is no issue, even with the TypeScript errors, etc.
Desired Behavior
TypeScript correctly recognizes the proper endpoint (profiles.get.ts since I'm calling it with get)
Data can be fetched on mount from the API without the use of setTimeout

How to fetch data with slot in Vue?

I have a code block like this.
<template slot="name" slot-scope="row">{{row.value.first}} {{row.value.last}}</template>
Also I have a header.
{ isActive: true, age: 38, name: { first: 'Jami', last: 'Carney' } },
{ isActive: false, age: 27, name: { first: 'Essie', last: 'Dunlap' } },
{ isActive: true, age: 40, name: { first: 'Thor', last: 'Macdonald' } },
This code is running clearly but I want to show data from my API. Which terms do I need to know? I used Axios before in React. Where can I define Axios method? Do I need to change the template slot instead of :v-slot ?
Although you can make API calls directly from inside the component code, it does not mean that you should. It's better to decouple API calls into a separate module.
Here's a good way to do it which properly follows Separation of Concern (SoC) principle:
Create a directory services under src if it's not already there.
Under services, create new file named api.service.js.
api.service.js
import axios from 'axios';
const baseURL = 'http://localhost:8080/api'; // Change this according to your setup
export default axios.create({
baseURL,
});
Create another file peopleData.service.js
import api from './api.service';
import handleError from './errorHandler.service'; // all the error handling code will be in this file, you can replace it with console.log statement for now.
export default {
fetchPeopleData() {
return api.get('/people')
.catch((err) => handleError(err));
},
// All other API calls related to your people's (users'/customers'/whatever is appropriate in your case) data should be added here.
addPerson(data) {
return api.post('/people', data)
.catch((err) => handleError(err));
},
}
Now you can import this service into your component and call the function.
<template>
... Template code
</template>
<script>
import peopleDataService from '#/services/peopleData.service';
export default {
data() {
return {
rows: [],
};
},
mounted() {
peopleDataService.fetchPeopleData().then((res) => {
if (res && res.status == 200) {
this.rows = res.data;
}
});
},
}
</script>
You haven't given us any idea about your current setup. If you're using Vue-Router, it's better to fetch data in navigation guards, especially if your component is relying on the data: Data Fetching
Simply shift the code from mounted() into a navigation guard. this may not be available, so you will have to use next callback to set rows array, it's explained in the link above.
You can use Axios in methods or mounted.
mounted(){
this.loading = true;
axios
.get(`${this.backendURL}/api/v1/pages/layouts` , authHeader())
.then(response => (this.layouts = response.data.data))
.catch(handleAxiosError);
}
methods: {
/**
* Search the table data with search input
*/
uncheckSelectAll(){
this.selectedAll = false
},
onFiltered(filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length;
this.currentPage = 1;
},
handlePageChange(value) {
this.currentPage = value;
axios
.get(`${this.backendURL}/api/v1/pages?per_page=${this.perPage}&page=${this.currentPage}` , authHeader())
.then(response => (this.pagesData = convert(response.data.data),
this.pagesDataLength = response.data.pagination.total));
},
handlePerPageChange(value) {
this.perPage = value;
this.currentPage = 1;
axios
.get(`${this.backendURL}/api/v1/pages?per_page=${this.perPage}&page=${this.currentPage}` , authHeader())
.then(response => (this.pagesData = convert(response.data.data),
this.pagesDataLength = response.data.pagination.total));
},
deletePage(){
this.loading = true
this.$bvModal.hide("modal-delete-page");
window.console.log(this.pageIdentity);
if (!roleService.hasDeletePermission(this.pageIdentity)){
return;
}
axios
.delete(`${this.backendURL}/api/v1/pages/${this.page.id}` , authHeader())
.then(response => (
this.data = response.data.data.id,
axios
.get(`${this.backendURL}/api/v1/pages?per_page=${this.perPage}&page=${this.currentPage}` , authHeader())
.then(response => (this.pagesData = convert(response.data.data),
this.pagesDataLength =
response.data.pagination.total)),
alertBox(`Page deleted succesfully!`, true)
))
.catch(handleAxiosError)
.finally(() => {
this.loading = false
});
}

Multiple commits in vuex action not triggering mutation

I'm attempting to set a loading status on a component which will display a loading graphic while the request is being processed. I have this working with a call to an API, returning a promise from a fetch to a REST interface. However, when attempting this with a simple call to a service it only seems to call the last mutation
In the vuex store:
var initialState = {
status: { loading: false },
all_clients: [],
current_client: {
loaded: false,
}
}
export const fund = {
namespaced: true,
state: initialState,
actions: {
getClientOverview({ commit }, client_group_id) {
commit('loadingRequest');
clientService.getClientOverview(client_group_id)
.then(
clientInfo => {
commit('loadingSuccess')
},
error => {
commit('loadingError', error);
}
)
}
},
mutations: {
// Mutations to set loading and error statuses
loadingRequest(state) {
state.status = { loading: true };
},
loadingSuccess(state) {
state.status = { loading: false };
},
loadingError(state,error) {
state.status = { loading: false };
store.dispatch('alert/error', "Error Loading Client: " + error)
},
}
and in the corresponding client service that is called:
function getClientOverview(client_group_id) {
return new Promise((resolve,reject) => {
try {
// Note here that client.state.all_clients is an array of objects already set in the store
let clientDetail = client.state.all_clients.filter(function(item) {
return item.client_group_id === client_group_id;
});
// THIS PAUSE CODE JUST ADDED TO SLOW THINGS DOWN A BIT ON FRONT END
// SO I CAN SEE IF MUTATIONS ARE WORKING CORRECTLY
var d = new Date();
var d2 = null;
do { d2 = new Date(); }
while(d2-d < 2000);
// TO HERE
if(clientDetail.length == 0){
reject("Client Information not found")
}
resolve(clientDetail[0])
}catch(error){
reject(error)
}
})
}
A client is being returned as expected, as a resolved promise, but I only see 'loading' being set to false on the front end, it doesn't set to true while the service is running its process.
If I remove the commit('loadingSuccess') line then I can see the original commit being called as expected and loading being set to true - it seems as though only one commit is being called per action
Any help with this would be gratefully appreciated!
Thanks

Axios GET not including params in Nuxt template

I want to pass an id to axios so that I can switch url dynamically.
My axios request in my template is as follows:
async asyncData({ params }) {
const { data } = await axios.get('http://localhost:8000/api/', {
params: {
id: 1
}
})
return { data }
}
The request being passed to my api is:
GET /api/?id=1
but I need
GET /api/1
What is happening here?
It looks like the asyncData function is called once when the page is loaded. I am still no wiser as to why it does not accept params in the way outlined in the docs and numerous tutorials, but it would not refresh the page because it is never called again.
To refresh the page data with a new api call, you need to return the axios promise from within the methods part of the export. The code below does the axios get request first, then adds or subtracts 1 from the id with plus and minus functions.
<script>
import axios from 'axios'
export default {
head() {
return {
title: 'Weather'
}
},
data: function() {
return { counter: 1 }
},
methods: {
plus: function(counter, data, datalength) {
this.counter += 1
axios.get('http://localhost:8000/api/' + this.counter).then(res => {
console.log(this.counter)
console.log(res.data)
return (this.data = res.data)
})
},
minus: function(counter, data) {
if (this.counter >= 2) {
this.counter -= 1
axios.get('http://localhost:8000/api/' + this.counter).then(res => {
console.log(this.counter)
console.log(res.data)
return (this.data = res.data)
})
} else {
this.counter = 1
}
}
},
async asyncData({ params, counter }) {
let { data } = await axios.get('http://localhost:8000/api/1')
return { data }
}
}
</script>
If anybody wants to elaborate or post a better solution, please go ahead - but I'm posting this because I searched so many tutorials and nothing worked until I found a way to interpret the documentation, which is certainly not beginner-friendly.

"Maximum call stack size exceeded" by passing the data to the Vuex-state

I am fetching the data from a MongoDB through sending GET requests to my API. Then I loop through the response.data and in each response.data through its properties to push the data which I need to nextArray. And this nextArray should be passed to the schedulingQuality-state in the Vuex. That's how it looks like:
methods: {
...mapActions(
['setSchedulingQuality']
),
get_data() {
const nextArray = [];
for(let i in this.SelectedtValues) {
axios.get('http://127.0.0.1:5000/getexp/'+this.SelectedtValues[i])
.then(res => {
for(let n in res.data) {
nextArray.push(res.data[n].output)
}
}
)}
console.log(nextArray);
},
computed: {
...mapGetters(
['schedulingQuality','selectedValues']
),
SelectedtValues() {
return this.$store.getters.selectedValues;
} ,
schedulingQuality() {
return this.schedulingQuality;
}
}
When I'm printing out the nextArray then it seems to be ok. I'm getting a [] on the console and after I click on it the correct content appears with a small i icon which tells: "Value below was evaluated just now". However I am not able to print out the items of this Array separately, each of them has a value of undefined, when I try that.
But my main problem is that it throws an Maximum call stack size exceeded error, when I'm trying to pass it to my Vuex-state in the code above befor printing out, like:
this.setSchedulingQuality(nextArray)
Here is my Vuex-code:
import Vuex from "vuex";
import axios from "axios";
const createStore = () => {
return new Vuex.Store({
state: {
schedulingQuality: [],
},
mutations: {
SchedulingQuality(state, payload) {
state.schedulingQuality = payload;
}
},
actions: {
setSchedulingQuality({commit}, payload){
commit('SchedulingQuality',payload)
}
},
getters: {
schedulingQuality(state) {
return state.schedulingQuality;
}
}
});
};
export default createStore;
My questions are:
Why it is not possible to print out the Array items separately?
Why I'am getting this error
And how can I fix it?
Thank you for your time.
axios call is asynchronous. At the time you call console.log(nextArray), axios function is not finished yet. That's why you got empty array.
You call multiple api asynchronously, I suggest you check out Promise.all
get_data() {
const nextArray = [];
Promise.all(this.SelectedtValues.map(value => {
return axios.get('http://127.0.0.1:5000/getexp/' + value)
})).then(results => {
results.map(res => {
for(let n in res.data) {
nextArray.push(res.data[n].output)
}
})
console.log(nextArray);
}).catch(err => {
console.error(err)
})
}