My function is not loading in the right way - javascript

When I load my page my datacollection is null or undefined, but I want my data from the api to be filled in as soon as the site loads.
<script>
import LineChart from './LineChart.js'
import axios from 'axios'
export default {
name: 'Graph',
components: {
LineChart
},
data () {
return {
ratingOne: null,
ratingTwo: null,
ratingThree: null,
datacollection: null
}
},
created: function () {
console.log(this.datacollection)
this.fillData()
},
mounted () {
},
methods: {
getZeroes () {
axios.get('MYAPI').then(response => {
this.ratingOne = response.data.Rates.filter(rates =>
rates.rate === 0).length
return this.ratingOne
})
},
fillData () {
this.getOnes()
console.log(this.getZeroes())
this.getOnes()
this.getTwos()
this.datacollection = {
labels: ['Dårlig oplevelse', 'Okay oplevelse', 'Rigtig god
oplevelse'],
datasets: [
{
backgroundColor: ['#FF0000', '#D3D3D3', '#00CC00'],
data: [this.ratingOne, this.ratingTwo, this.ratingThree]
}
]
}
}
}
}
</script>
When i use a click function it works and it loads my graph with the data I want, but it does not work when my page I loaded. Can anyone tell me why?
When i console log "this.getZeroes())" it just tells me "undefined"
and when i click my button with update
<button #click="fillData()">Randomize</button>
it works

getZeroes, getOnes, getTwos are asynchronous functions. After getZeroes function and such are run, the data is not ready. That's why dataCollection doesn't have the right data.
You need to you promise to wait for the response finish, and update the data to the chart.
getZeroes will return a promise.
getZeroes () {
return axios.get('MYAPI').then(response => {
this.ratingOne = response.data.Rates.filter(rates => rates.rate === 0).length
return this.ratingOne
})
},
in the fillData. We must wait them to finish by using then
fillData () {
this.getOnes()
.then(() => this.getZeroes())
.then(() => this.getTwos())
.then(() => {
this.datacollection = {
labels: ['Dårlig oplevelse', 'Okay oplevelse', 'Rigtig god oplevelse'],
datasets: [
{
backgroundColor: ['#FF0000', '#D3D3D3', '#00CC00'],
data: [this.ratingOne, this.ratingTwo, this.ratingThree]
}
]
}
})
}
Or we can even run the gets parallel and get data at once by using Promise.all
Promise.all([this.getOnes(), this.getTwos(), this,getThrees()])
.then(() => { /* the rest here */ })

Related

Pinia|Vue3 I can't access the property of the object that returned from the Pinia action

first of all I am using the Mockjs to simulate the backend data:
{
url: "/mockApi/system",
method: "get",
timeout: 500,
statusCode: 200,
response: { //
status: 200,
message: 'ok',
data: {
'onlineStatus|3': [{
'statusId': '#integer(1,3)',
'onlineStatusText': '#ctitle(3)',
'onlineStatusIcon': Random.image('20*20'),
'createTime': '#datetime'
}],
'websiteInfo': [{
'id|+1': 1,
}]
}
}
}
the data structure would be: https://imgur.com/a/7FqvVTK
and I retrieve this mock data in Pinia store:
import axios from "axios"
import { defineStore } from "pinia"
export const useSystem = defineStore('System', {
state: () => {
return {
systemConfig: {
onlineStatus: [],
},
}
},
actions: {
getSystemConfig() {
const axiosInstance = axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.baseURL = '/mockApi'
return config
}, function (error) {
// Do something with request error
return Promise.reject(error);
})
axios.get('/system/').then(res => {
this.systemConfig.onlineStatus = res.data.data.onlineStatus
})
// console.log(res.data.data.onlineStatus)
axios.interceptors.request.eject(axiosInstance)
}
}
})
I use this store in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]
},
},
components: {UserOnlineStatus }
}
template in Profile.vue I import the child component userOnlineStatus.vue
<UserOnlineStatus :userCurrentOnlineStatus="userData.onlineStatus">
{{ showUserOnlineStatusText }}
</UserOnlineStatus>
here is what I have got https://imgur.com/fq33uL8
but I only want to get the onlineStatusText property of the returned object, so I change the computed code in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]['onlineStatusText']//👀I chage it here!
},
},
components: {UserOnlineStatus }
}
but I will get the error in the console and it doesn't work:
https://imgur.com/Gb68Slk
what should I do if I just want to display the specific propery of the retrived data?
I am out of my wits...
I have tried move the store function to the child components, but get the same result.
and I google this issue for two days, nothing found.
Maybe it's because of I was trying to read the value that the Profile.vue hasn't retrieved yet?
in this case, how could I make sure that I have got all the value ready before the page rendered in vue3? Or can I watch this specific property changed, then go on rendering the page?
every UX that has data is coming from remote source (async data) should has spinner or skeleton.
you can use the optional chaining for safe access (if no time to await):
return this.getUserOnlineStatus?.[this.getUserOnlineStatusIndex - 1]?.['onlineStatusText']

http request await sync in repetitive function

I have a problem with a repetitive function with an HTTP request.
When I call the getDocument() function, a result appears at the end of the console but unfortunately not in the frontend. I get a new json array by the call where also the render() function should be applied.
I render the ouput after each run of the render() function in the HTML view.
It seems to me that the HTTP.request is too slow and can't keep up with the repeating function. I have read up a bit on async await but am still having problems using it correctly.
var jsonArray = [
{
etype: 'normal',
content: 'renderContent'
},
{
etype: 'extern',
id: 'MyIdToDocument',
subcontent: [
{
etype: 'normal',
content: 'renderContent'
}
]
},
{
etype: 'normal',
content: 'renderContent'
}
]
jsonArray.forEach(object => {
this.render(object)
});
function render(object) {
if(object.etype ==='normal') {
object.subcontent.forEach( subcontentItem => {
output = this.render(subcontentItem)
})
}
if(object.etype ==='extern') {
output = this.getDocument(object.id)
}
return output;
}
function getDocument(id) {
this.httpClient.post('myAPiUrl/', id).subscribe(response => {
response.forEach(object => {
this.render(object)
});
})
}

apollo client offsetLimitPagination not working

I have a hook..
export function useLazyProposalList() {
const [getQueueData, { loading, data, error, fetchMore }] = useLazyQuery(PROPOSAL_LIST, {
fetchPolicy: 'no-cache',
});
const proposalList = React.useMemo(() => {
if (!data) {
return null;
}
return transformProposals(data);
}, [data]);
return {
getQueueData,
fetchMore,
loading,
data: proposalList,
error,
};
}
In the component
const {
getQueueData,
data: queueData,
fetchMore: fetchMoreProposals,
// loadMore: loadMore,
} = useLazyProposalList();
If user clicks on fetch more button, I call: fetchMoreProposals .
await fetchMoreProposals({
variables: {
offset: visibleProposalList.length,
},
});
but this doesn't update my data. I read that we should use offsetLimitPagination, but my data from query is not array itself. It's like this: queue { id: '1', items:[] } and because of this, offsetLimitPagination doesn't work. So I tried merge
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
queue: {
keyArgs: false,
merge(existing, incoming) {
console.log(existing, incoming);
if (!incoming) return existing;
if (!existing) return incoming;
},
},
},
},
}
but in the console, it just prints refs instead of real data.
What could be the issue ?

Vue js Laravel - Axios making Multiple ( 2 ) Requests when changing pages , Category and Subcategory

I'm building an application with Vue on the frontend and Laravel PHP on the backend. Its a single page app (SPA).
When changing pages, sometimes - not always - axios makes two requests for the same page. I'm having trouble figure it out what is happening.
When the link changes I have two watchers, one for the top category and another for the sub-category. They trigger the created () hook that calls the loadData method.
If I change the main category and sub category ( Example: from 1/5 to 2/31 ), the loadData method is called two time. How can I fix this ?
Google Network Tab (The .json request does not represent the same type of page that I'm referring above, only the numbers ) :
<script>
import axios from 'axios'
import { mapGetters } from 'vuex'
import Form from 'vform'
export default {
data() {
return {
products: {
cat : {} ,
products : []
},
productsShow: '',
quickSearchQuery: '',
loadeddata : false,
}
},
methods: {
loadMore () {
this.productsShow += 21
},
loadData () {
if ( this.$route.params.sub_id ) {
axios.get('/api/cat/' + this.$route.params.cat_id + '/' + this.$route.params.sub_id).then(response => {
this.products = response.data
this.productsShow = 21
this.loadeddata = true
}).catch(error => {
if (error.response.status === 404) {
this.$router.push('/404');
}
});
} else {
axios.get('/api/cat/' + this.$route.params.cat_id ).then(response => {
this.products = response.data
this.productsShow = 21
this.loadeddata = true
}).catch(error => {
if (error.response.status === 404) {
this.$router.push('/404');
}
});
}
},
computed: {
...mapGetters({locale: 'lang/locale', locales: 'lang/locales' , user: 'auth/user'}),
filteredRecords () {
let data = this.products.products
data = data.filter( ( row ) => {
return Object.keys( row ).some( ( key ) => {
return String( row[key] ).toLowerCase().indexOf(this.quickSearchQuery.toLowerCase()) > -1
})
})
return data
}
},
created() {
this.loadData()
},
watch: {
'$route.params.cat_id': function (cat_id) {
this.quickSearchQuery = ''
this.loadData()
},
'$route.params.sub_id': function (sub_id) {
this.quickSearchQuery = ''
this.loadData()
}
}
}
</script>
So I see that you figured the issue by yourself.
To fix that, you can delay the loadData function (kind of debounce on the tail) so if it's being called multiple times in less than X (300ms?) then only the last execution will run.
try:
loadData () {
clearTimeout(this.loadDataTimeout);
this.loadDataTimeout = setTimeout(()=> {
//The rest of the loadData function should be placed here
},300);
}
Resolved :
watch: {
'$route.params': function (cat_id, sub_id) {
this.quickSearchQuery = ''
this.loadData()
},

Conditional get request in vue for rendering a subcomponent scoped

When I click a profile (of an author) component, I can't figure out how it should render a scoped sub-component, listing the main entities of the app, so-called fabmoments (containers for 3D print information).
My current solution looks like this:
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
// search: '',
localAuthor: '',
fabmoments: []
}
},
created () {
this.localAuthor = this.author
if (typeof localAuthor !== 'undefined') {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
This renders all in the profile, where it should render a list scoped to the current profile's author.
And it renders nothing in the home (without receiving the prop), where it should render all.
I am not much of star in JavaScript. What am I doing wrong?
UPDATE
This works as a solution, though not very elegant.
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
fabmoments: []
}
},
created () {
if (this.author.id >= 0) {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
Not sure which part is wrong, but you may definitely debug your code to find out why fabmoments is empty array assuming there is no error occurred yet.
There are three parts to debug:
http response -- to check if data is properly returned
this -- to check if this pointer still points at the component
template -- to check if fabmoments are correctly bind to the element
At last, it would be better to separate your http request logics from your components.
Good luck!

Categories

Resources