how to for loop data from localStorage nuxtjs - vue.js

this my code previously
<ul>
<li v-for="item in data.data">{{ item.name }}</li>
</ul>
export default {
layout: 'main',
async asyncData({ $axios }) {
const data = await $axios.$get('XXXXXXXXXXXXXXXXXXXXX');
return { data };
}
}
this can work but when i change the page and turnback to this page
axios send request again i need one time request and save data response to cache (localStorage)
now i try this code
export default {
layout: 'main',
mounted() {
this.starter()
},
methods: {
starter() {
let room = JSON.parse(localStorage.getItem('game_room'))
if (!room) {
fetch('XXXXXXXXXXXXXXXXXXXXX')
.then(res => res.json())
.then(json => {
localStorage.setItem('game_room', JSON.stringify(json))
let room = JSON.parse(localStorage.getItem('game_room'));
console.log('Response ', room.data[0].title)
});
} else {
console.log('Caching ', room.data[0].title)
}
}
}
}
now this response data save to localStorage but i dont know how to use data to v-for like the my code previously

Related

Progress bar with Vue and Axios object

I would like to create an upload progress bar with Axios.
Everything is working fine with server sending and response.
The problem is that I don't know how to capture the progress percentage (which is correctly calculated) from my exported object.
My file upload.js:
import axios from 'axios'
const baseUrl = 'http://localhost:80/upload.php'
const config = {
Headers: {'Content-Type': 'multipart/form-data'},
onUploadProgress: progressEvent => {
return parseInt(Math.round((progressEvent.loaded / progressEvent.total) * 100))
}
}
export default {
send (data) {
return axios.post(baseUrl, data, config)
}
}
My Vue component:
<template>
<div>
<label>File:</label>
<input type="file" id="file" ref="file" #change="changeFile()" />
<button #click="submit()">Upload</button>
<br />
<progress max="100" :value.prop="uploadPercentage"></progress>
</div>
</template>
<script>
import upload from '../services/upload.js'
export default {
name: 'Upload',
data: () => ({
file: null,
uploadPercentage: 0
}),
methods: {
submit () {
const formData = new FormData()
formData.append('file', this.file)
upload.send(formData)
.then(res => {
console.log(res.data)
})
.catch(() => {
console.log('Failure')
})
},
changeFile () {
this.file = this.$refs.file.files[0]
}
}
}
</script>
How to retreive, from the component submit method, the info sent by the onUploadProgress in order to update the data uploadPercentage?
Thanks.
Regards.
You need to pass a function to your send operation that will be called later.
See example below
const config = {
Headers: {'Content-Type': 'multipart/form-data'},
onUploadProgress: progressEvent => {
var progress= parseInt(Math.round((progressEvent.loaded / progressEvent.total) * 100));
if (config.onProgress)
config.onProgress(progress);
}
}
export default {
send (data, onProgress) {
config.onProgress= onProgress;
return axios.post(baseUrl, data, config)
}
}
Then you upload code will be
upload.send(formData,(pogress)=>{
// Update your uploadPercentage here
})
.then(res => {
console.log(res.data)
})
.catch(() => {
console.log('Failure')
})

watchEffect does not run when inserting new data into a list

I am making an application which the user can create projects and assign to a user, for that I am consuming an api made in go, the form sends the data to an api to persist the information and then the api returns the input data to me record the data in the array.
My form in Vue 3:
export default {
setup() {
const name = ref('');
const description = ref('');
const endProject = ref('');
const user = ref(0);
const users = ref();
const listProjects = inject('listProjects');
const router = useRouter();
fetch(`http://localhost:8000/api/v1/users/`)
.then((data) => data.json())
.catch((error) => console.error(error))
.then((response) => {
users.value = response;
});
const saveProject = async () => {
if (
name.value === '' ||
description.value === '' ||
endProject.value === '' ||
user.value === ''
) {
console.log('error');
return;
}
await fetch(`http://localhost:8000/api/v1/projects`, {
method: 'POST',
body: JSON.stringify({
name: name.value,
description: description.value,
end_date: new Date(endProject.value.replace('-', '/')),
user: {
id_user: user.value,
},
}),
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.catch((error) => console.error('error:', error))
.then((data) => {
listProjects.value.push(data);
});
router.push({
name: 'Project',
});
};
return {
name,
description,
endProject,
user,
users,
saveProject,
};
},
};
</script>
The problem happens is when I want to update the api which brings all the projects, I consider that the error occurs because the api where it registers and brings all the information are in different views, one in a form and the other in the view principal.
This is my App.vue
<script>
import { ref } from '#vue/reactivity';
import Navbar from './components/layout/Navbar.vue';
import Sidebar from './components/layout/Sidebar.vue';
import { provide, watchEffect } from '#vue/runtime-core';
export default {
components: { Navbar, Sidebar },
setup() {
const listProjects = ref();
watchEffect(() => {
console.log('bdfbdfb');
fetch(`http://localhost:8000/api/v1/projects`)
.then((res) => res.json())
.catch((err) => console.log(err))
.then((response) => {
listProjects.value = response;
});
});
provide('listProjects', listProjects);
return {
listProjects,
};
},
};
</script>
From comments:
I am expecting that every time the view where the array is pushed is done, App.vue executes the watchEffect
Well it is not how watchEffect works. watchEffect is designed to execute some "side effects" (like fetch call) that depend on some reactive data (for example URL for fetch is created using some ID ref). Yours watchEffect does not depend on any reactive data. It only assign new value to a listProjects ref. (in other words, result of the function you pass to watchEffect does not depend on listProjects ref value at all)
You can debug watchEffect (and watch) using onTrack option. See example bellow. 1st watchEffect does not trigger onTrack because it does not read any reactive data. 2nd does.
const app = Vue.createApp({
setup() {
const projects = Vue.ref([])
Vue.watchEffect(() => {
projects.value = []
}, {
onTrack(e) {
console.log('watchEffect 1 is tracking projects')
}
})
Vue.watchEffect(() => {
console.log(projects.value)
}, {
onTrack(e) {
console.log('watchEffect 2 is tracking projects')
}
})
}
}).mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
</div>

Axios/Vue/Nuxt - Find out when all API calls are finished

I want to make several API calls to get data into a component. I created a PostService.ts that looks like this:
const apiClient = axios.create({
baseURL: '/api/v1',
})
export default {
async getPosts() {
const { data }: { data: Post[] } = await apiClient.get('/posts')
// transform data ...
return data
},
async getTags() {
const { data }: { data: Tag[] } = await apiClient.get('/tags')
return data
},
async getComments() {
const { data }: { data: Comment[] } = await apiClient.get('/comments')
return data
},
}
This is my posts.vue:
<template>
<div>
<div v-if="dataLoaded">
content
</div>
<div v-else>
loading...
</div>
</div>
</template>
<script>
finishedApiCalls = 0
get dataLoaded() {
return this.finishedApiCalls === 3
}
created() {
PostService.getPosts()
.then((posts) => {
this.posts = posts
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
PostService.getTags()
.then((tags) => {
this.tags = tags
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
PostService.getComments()
.then((comments) => {
this.comments = comments
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
}
</script>
The key point is that I want to display a loading spinner as long as the data has not been loaded. Is it recommended to make the API calls from created()? What would be a more elegant way to find out when all calls are finished? It does not feel right to use the finishedApiCalls variable.
I recommend using Nuxt's fetch method along with Promise.all() on all your async PostService fetches:
// MyComponent.vue
export default {
fetch() {
return Promise.all([
PostService.getPosts().then((posts) => ...).catch((error) => ...),
PostService.getTags().then((tags) => ...).catch((error) => ...),
PostService.getComments().then((comments) => ...).catch((error) => ...)
])
}
}
Nuxt provides a $fetchState.pending prop that you could use for conditionally rendering a loader:
<template>
<div>
<Loading v-if="$fetchState.pending" />
<div v-else>My component data<div>
</div>
</template>
You can use Promise.all for this kind of requirements.
this.loading = true
Promise.all([PostService.getPosts(), PostService.getTags(), PostService.getComments()])
.then(values => {
let [posts, tags, comments] = values
this.posts = posts
this.tags = tags
this.comments = comments
//Here you can toggle your fetching flag like below
this.loading = false
})
You can use Promise.all(). This will wait till all resolves or if 1 fails.
With async / await you can make it "synchronous"
data() {
return {
loaded: false
}
},
async created() {
let [posts, tags, comments] = await Promise.all([PostService.getPosts(), PostService.getTags(), PostService.getComments()])
this.posts = posts;
this.tags = tags;
this.comments = comments;
this.loaded = true;
}

Copy of store not updated when mounted Async axios

I have been struggling with this issue for a day now. I want to make a copy of the store for user into userCopy so that it can be edited by the user without causing a mutation. My problem is that even though I am using the mounted hook, userCopy only returns an empty store state.
pages/settings/_id.vue
<template>
<div>
{{ user }} // will display the whole object
{{ userCopy }} // will only display empty store object
</div>
</template>
<script>
import { mapState } from 'vuex'
import _ from 'lodash'
data() {
return {
userCopy: {}
}
},
computed: {
...mapState({ user: (state) => state.staff.user })
},
created() {
this.$store.dispatch('staff/fetchUser', this.$route.params.id)
},
mounted() {
this.$data.userCopy = _.cloneDeep(this.$store.state.staff.user)
},
</script>
store/staff.js
import StaffService from '~/services/StaffService.js'
export const state = () => ({
user: {
offers: '',
legal: ''
}
})
export const mutations = {
SET_USER(state, user) {
state.user = user
},
}
export const actions = {
fetchUser({ commit, getters }, id) {
const user = getters.getUserById(id)
if (user) {
commit('SET_USER', user)
} else {
StaffService.getUser(id) // StaffService users axios get call
.then((response) => {
commit('SET_USER', response.data)
})
.catch((error) => {
console.log('There was an error:', error.response)
})
}
},
}
export const getters = {
getUserById: (state) => (id) => {
return state.staff.find((user) => user.id === id)
}
}
Even using this mounted method did not solve the issue. The userCopy object still returns empty.
mounted() {
this.$store
.dispatch('staff/fetchUser', this.$route.params.id)
.then((response) => {
this.userCopy = this.$store.state.staff.user
})
},
It seems that the mounted() is called before your network request get solved.
To fix this, I suggest to do like this.
First:
if (user) {
console.log('user found',user)
commit('SET_USER', user)
return user
} else {
console.log('user not found')
//RETURN the Axios Call here
return StaffService.getUser(id) // StaffService users axios get call
.then((response) => {
commit('SET_USER', response.data)
//return the response here, after committing
return response.data
})
then in your component
mounted() {
this.$store
.dispatch('staff/fetchUser', this.$route.params.id)
.then((response) => {
console.log(response)
this.userCopy = response
})
}

Vue-Router: Cannot read property '$route' of undefined - VueJS

Please review my code.
<template>
<div class="row flex">
<div class="col-md-6 home_feed">
<post-detail :posts="posts"></post-detail>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
async asyncData (params) {
let { data } = await axios.get('http://localhost:8000/api/v1/users/' + this.$route.params.id + '/')
return {
posts: data
}
},
components: {
'post-detail': Item
}
}
</script>
I got this error: Cannot read property '$route' of undefined when I asyncdata from params.id, but when I type: console.log(this.$route.params.id), it goes right. How can I fix this
if you want to load data from server (from browser) in mounted lifecycle try this:
export default {
data() {
return {
data: {}
}
},
mounted() {
this.asyncData();
},
methods: {
async asyncData ({ route }) {
let { data} = await axios.get('http://localhost:8000/api/v1/users/' + this.$route.params.id + '/')
this.data = data
}
}
}
Response from server will be available in response variable.
For SSR you can do :
async asyncData ({ store, route }) {
let { data} = await axios.get('localhost:8000/api/v1/users/'; + route.params.id + '/')
return {
posts: data
}
}
asyncData will be called before the components are instantiated, and it doesn't have access to this. (see https://ssr.vuejs.org/en/data.html Logic Collocation with Components for details)
For SSR you can change
<script>
async asyncData ({ store, route }) {
let { data} = await axios.get('localhost:8000/api/v1/users/' + this.$route.params.id + '/')
return {
posts: data
}
}
</script>
to
<script>
async asyncData ({ route }) {
let { data} = await axios.get('localhost:8000/api/v1/users/' + route.params.id + '/')
return {
posts: data
}
}
</script>
According to the nuxt tutorial you can not have access to this inside asyncData because it is called before initiating the component. Nuxt Documentation
#Mikhail
This code is success:
export default {
data() {
return {
data: {}
}
},
mounted() {
this.asyncData();
},
methods: {
async asyncData ({ route }) {
let { data} = await axios.get('http://localhost:8000/api/v1/users/' + route.params.id + '/')
this.data = data
}
}
}
But when get API parent-children data like this: {{data.user.username}}, data.user goes undefined. So API data goes error.
I use Nuxt and your code for SSR not work:
Error: Cannot read property $route of undefined
<script>
async asyncData ({ store, route }) {
let { data} = await axios.get('localhost:8000/api/v1/users/'; + this.$route.params.id + '/')
return {
posts: data
}
}
</script>