Problem with rendering data and heavy data loading - vue.js

The first problem is that when getDetails(‘multiple’, ‘2’) function is called inside HTML, it takes some time until de data is displayed from v-for.
The second problem is that when I call the console.log(userDetails) from inside of anotherFunction() I got the undefined answer. It doesn’t wait for the this.getDetails(‘multiple’, ‘1’) to execute completely.
How can I improve the time for rendering, or should I use another way to display de data?
How can I make the second function to wait until the first function is complete?
VUE version: 2.7.10
<div id="app">
<p v-for="item in userDetails">item is displayed</p> //
<button #click="anotherFunction()">Click Me!</button>
</div>
<script>
export default {
name: 'App',
data: {
userDetails: []
}
}
// axios function
getDetails(actionType, idUser) {
axios.post("https://example.com/link", {
Username: username
}).then(response => {
const result = response.data;
// push data into variable
this.userDetails.push(result[0])
}).catch(error => {
this.showError('Error', 4000);
console.error('Error:' + error);
});
// another function from where I want to call the axios function
anotherFunction() {
this.getDetails('multiple', '1')
// call the userDetails into another function will output "undefined"
console.log(this.userDetails);
}

Related

Issue with method not returning values in interpolation nuxjs/vue

In my nuxtjs app static folder I have a file called data.json
in my component I use this data like so
<script>
import data from '~/static/data.json';
export default {
data ({ params }) {
return {
data
}
}
}
</script>
now I have a method that will basically take values from that data and create a little counting up animation like so
methods: {
countUp(value) {
for (let i = 0; i <= value; i++) {
setTimeout(() => {
return i;
}, 100);
}
}
}
and in my template I am calling it like so
<template>
<div>
<p>{{countUp(data.number)}}</p>
</div>
</template>
now the expected result is for the number to quickly change from 0 to the value but nothing is being printed on the dom if I inspect the html element its empty??
What am I doing wrong??
setTimeout doesn't work the way you think it does:
You can't return a value from inside the callback function; nothing is being returned from the countUp method.
The call to setTimeout doesn't block, meaning it will return immediately after being called and the callback function passed to it is scheduled for execution asynchronously after the timeout has passed. So every setTimeout call in the for loop will be executed all at once after 100 ms instead of staggered.
You will need to store the current counter value as data on the component so Vue knows to rerender when its value is changed.
The simplest example I can provide follows, but you might want to encapsulate the logic in a separate reusable component.
const value = 50
new Vue({
el: '#app',
data: {
counter: 0,
},
methods: {
countUp() {
const interval = setInterval(() => {
this.counter++
if (this.counter >= value) {
clearInterval(interval)
}
}, 100)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="countUp">Count Up</button>
{{ counter }}
</div>

How to retrieve and display a single record with Vue and axios

I have a basic CRUD rails 5.2 API with a Story model. I am building a Vuejs front end to consume it. Currently, The index view at /stories successfully pulls in data from the server. I can also add stories to the database via NewStory.vue at stories/new. I am trying now to show a single story on a page at stories/:id. The api server currently shows the single result I need at v1/stories/:id.
here is what I have at services/Api.js:
import axios from 'axios'
export default() => {
return axios.create({
baseURL: `http://localhost:3000/v1`
})
}
in StoriesService.js:
import Api from '#/services/Api'
export default {
fetchStories () {
return Api().get('stories')
},
addStory (params) {
return Api().post('stories', params)
},
getStory (params) {
return Api().get('/stories/1')
}
}
in ViewStory.vue:
<template>
<div class="stories">
<h1>Story</h1>
<div v-if="story" class="table-wrap">
<div>
<router-link v-bind:to="{ name: 'NewStory' }" class="">Add
Story</router-link>
</div>
<p>Title: {{story.attributes.title}}</p>
<p>Overview: {{story.attributes.description}}</p>
</div>
<div v-else>
The story with id:{{params}} does not exist <br><br>
<!-- <router-link v-bind:to="{ name: 'NewStory' }"
class="add_story_link">Add Story</router-link> -->
</div>
</div>
</template>
<script>
import StoriesService from '#/services/StoriesService'
export default {
name: 'story',
data () {
return {
title: '',
description: ''
}
},
mounted () {
this.getStory()
},
methods: {
async getStory (params) {
const response = await StoriesService.getStory(params)
this.story = response.data
console.log(this.story)
}
}
}
</script>
With the id of the record hard coded, in the Network tab, I see the request being made to the api and the correct record being retrieved.
However, if I change the getStory call to return Api().get('/stories/', params) I get a 304 response and can't retrieve data.
My question is how to get StoriesService.js to return localhost:3000/v1/stories/params.id, where params.id is the id of the story referenced in the url.
Currently you are not passing in any params to getStory, so you need to get them from the this.$route.params
async getStory () {
const response = await StoriesService.getStory(this.$route.params)
this.story = response.data
console.log(this.story)
}
Beside that axios only supports query string parameters so if your url looks like /stories/someId then you need to build it yourself in getStory:
getStory (params) {
return Api().get(`/stories/${params.id}`)
}
Additionally your data object is missing the story property:
data () {
return {
story: null,
title: '',
description: ''
}
},

Vue.js re-render static content

I'm new with Vue.js, and I notice some content re-render after changing any data that is not part of that content, here is an example:
https://jsfiddle.net/gustavompons/rtxqhyv2/1/
HTML
<div id="app">
<input v-model="foo1">
<div v-html="showFoo1()"></div>
<div v-html="showFoo2()"></div>
</div>
JS
new Vue({
el: '#app',
data: {
foo1: 'foo1',
foo2: 'foo2'
},
methods: {
showFoo1 () {
console.log('this is ok to execute on input')
return this.foo1
},
showFoo2 () {
console.log('this should NOT execute on input')
return this.foo2
}
}
})
So every time I type on the input, I get "this should NOT re-render on input" in the console, which I think it's not ok because there is no reason to execute that piece of code every time.
Is this the way Vue work or am I doing something wrong?
I'm using vue.js v2
The results of methods are not cached and will be executed every time the component is re-rendered. If you want caching and dependency tracking, use computed properties instead:
computed: {
showFoo1 () {
console.log('this is ok to execute on input')
return this.foo1
},
showFoo2 () {
console.log('this should NOT execute on input')
return this.foo2
}
}
And get rid of the () when accessing them.

Data Fetch in Vue.js Single File Component

my data fetch works fine when is used globally but once I stick in the single file component is not returning the items. What I'm doing wrong?
ladeditems.vue
<template>
<div>
<ul v-for="item in items">
<li>
{{item.title}}
</li>
</ul>
</div>
</template>
<script>
export default {
components: {'tiny-slider': VueTinySlider},
name: 'ladeditems',
data: {
items: null
},
methods: {
fetchData: function () {
let self = this
const myRequest = new Request('https://jsonplaceholder.typicode.com/posts')
fetch(myRequest)
.then((response) => { return response.json() })
.then((data) => {
self.items = data
// console.log(self.items)
}).catch( error => { console.log(error); });
}
},
mounted() {
this.fetchData()
}
}
</script>
Your data declaration is incorrect, it should be like this:
data: function () {
return {
items: null
}
}
This info is here: data. In short it has to be a function that returns an object. This should allow the property to be reactive to your changes.
Also worth noting that fetch isn't declared in the code you've provided so I assume it's a global declaration. If it isn't and it's a mixin then you'll need to scope it with this.fetch(...)
https://v2.vuejs.org/v2/api/#data
When defining a component, data must be declared as a function that returns the initial data object, because there will be many instances created using the same definition. If we use a plain object for data, that same object will be shared by reference across all instances created! By providing a data function, every time a new instance is created we can call it to return a fresh copy of the initial data.

VueJS: TypeError: Cannot read property of undefined when Reload

I have a page like this:
<template>
<div class="row flex">
{{posts.id}}
</div>
</template>
<script>
import axios from 'axios'
export default {
async asyncData ({ route }) {
let { data } = await axios.get('http://localhost:8000/api/v1/feeds/' + route.params.id + '/')
return {
posts: data
}
}
}
</script>
When I click link with hot reload (router-link), it display well. But when I reload this window, it appear in 1 seconds and disappear then.
Video: http://g.recordit.co/ht0a0K2X81.gif
Error Log:
How can I fix this?
Add a property to your data i.e dataLoaded: false. When your ajax request has finished, set this.dataLoaded = true. On your template add v-if="dataLoaded. This will mean the template data won't render until you're ready.
You could also do v-if="posts" as another way but I generally have a consistent dataLoaded prop available to do this.
Edit: I just looked at your example again and doing something like this would work:
<template>
<div class="row flex" v-if="posts">
{{posts.id}}
</div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
posts: null
}
}
methods:{
loadPosts () {
return axios.get('http://localhost:8000/api/v1/feeds/' + this.$route.params.id + '/')
}
},
created () {
this.loadPosts().then(({data}) => {
this.posts = data
})
}
}
</script>
I've removed the async and just setting posts when the axios request returns it's promise. Then on the template, it's only showing posts is valid.
Edit
You can also use your original code and just add v-if="posts" to the div you have in your template.