Data Fetch in Vue.js Single File Component - vue.js

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.

Related

Problem with rendering data and heavy data loading

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);
}

Vue Dynamic Form Values AND Keys, with pre existing value for render, why not rendering?

I am trying to create a dynamic 'quick input' form with Vue.
A simple text input that has a dynamic data key so that I can change what I'm submitting to axios. I couldn't figure out how to get a dynamic key name coming from a prop eg
data() {
return {
DYNAMIC-NAME-FROM-PROP: value
}
}
So I've got a values: {} bit of data that gets filled by the props instead.
The code below achieves everything EXCEPT pre-rendering the existing value.
See the comments next to v-model in the tempate:
<template>
<div class="relative">
<input
type="text"
v-model="values[fieldName]" // This is not rendering on load
// v-model="values[this.$props.field]" -> 'this' is null error #input
#keydown.enter.prevent="submit"
/>
</div>
</template>
<script>
export default {
props: ["userId", "field", "preFill"],
data() {
return {
values: {},
fieldName: this.$props.field,
};
},
mounted() {
this.values[this.$props.field] = this.$props.preFill;
},
methods: {
submit() {
axios.post(`/admin/${this.userId}/update`, this.values).then(response => {
// success
});
}
}
};
</script>
Am I going about this completely wrong?
Or am I 'nearly there' and just need to fix the v-model render issue?
To set a dynamic key name on an object in javascript, it turns out you can use square brackets, as in:
{
[keyName] : value
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
So my code is fixed by simply passing the prop as the key in the axios call:
submit() {
axios
.post(`/admin/${this.userId}/update`, {
[this.$props.field]: this.value
})
.then(response => {
// we out here
});
}

Do all properties need to be present on an object data property in vue?

Say I start out like this:
data() {
return {
user: {}
}
}
And then later on... I make an API call:
let response = await this.axios.get("/api/users/1.json");
this.user = response.data
Is this the proper way to assign the data from my API call to this.user?
Do all of the properties of user need to be defined first?
No, you don't have to declare all properties of user. Just having user in the data with any value different from undefined will suffice.
When the property is in the data, Vue will replace it with a getter and a setter. When you do:
this.user = newValue;
You are actually calling a setter that will map all properties of the newValue into getters themselves.
In the official docs, you can find more info on the Reactivity in Depth page.
Have a look below. Check the demo and the explanation in the image that shows the object that was created (and printed in the console).
var app = new Vue({
el: '#app',
data: {
other: null,
user: null
},
mounted() {
this.user = {
name: 'bob',
age: 10
}
}
})
console.dir(app)
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ other }}</p>
<p>{{ user }}</p>
</div>
No you dont need to put properties in it, you can do it like this. You create a method and make a axios inside it:
data() {
return {
user: {}
}
},
methods: {
axiosCall: function (){
axios.get("/api/users/1.json").then(response =>{
this.user = response.data
})
}
}

How to access data inside v-for and assign to autocomplete component?

I am new to vue and I've been struggling over this for a while. I have an external json array of airline details which I have looped over to only display countries. I would like to assign the countries to an autocomplete search function.
The autocomplete component i am using is https://vuejsexamples.com/simple-yet-feature-rich-autocomplete-component-for-vue-js/
I have modified it slightly to try and access the data with no luck.
Attempted to use computed to access the data
<vue-suggest
:list="simpleSuggestionList"
:filter-by-query="true">
</vue-suggest>
<div v-for="data in filteredMyJson" :key="data.id">
<div> {{ data.country }} </div>
</div>
import json from '../data/airports.json'
export default {
data: function () {
return {
myJson: json,
}
},
components: {
VueSuggest
},
computed: {
filteredMyJson: function () {
var self = this
return self.myJson
},
suggestionList () {
return this.data.country
}
},
methods: {
simpleSuggestionList () {
return this.suggestionList
}
}
}
Expected to show countries when begin typing, but instead returns nothing.

Computed object not updated using sync modifier

I have a computed property that simply formats the date:
computed: {
items() {
return this.licenseItems.map(function(license) {
license.expires_at = moment(license.expires_at).format('MM/DD/YYYY');
return license;
});
}
}
I pass licenseItems to a form component with the .sync modifier and emit update:field event from the form. In vue dev tools, I can see that licenseItems (data) is properly updated, but items (computed) is still showing the old data so no re-computation was performed.
I have noticed that if I remove the map and just return the licenseItems object from the computed property, it is updated. Is there some issue with Vue's computed property on mapped objects when using the sync modifier?
You should be aware that you're modifying underlying objects in your computed. From your fiddle:
computed: {
mapped: function() {
return this.items.map(function(item) {
let original = item.date;
item.date = moment(item.date).format('MM/DD/YYYY');
console.log(original + ' => ' + item.date);
return item;
});
}
}
Your incrementDates function also modifies the underlying objects directly.
Because each element of items is an object, item is a reference to that same object, so your routine updates the members of items itself. If you intend to modify the object values, you should use a watch instead of a computed. If you want to have a proper computed that does not modify data items, you need to deep copy the objects.
In my example below, you can see that the data value and the computed value are distinct, but the computed is based on the data. Also, the update event works with sync to update the value in the parent, rather than the value being updated directly in the component. You can enter any format of date that Date understands to set the value.
new Vue({
el: '#app',
data: {
licenseItems: [{
expires_at: Date.now()
}]
},
computed: {
items() {
return this.licenseItems.map(function(license) {
const newItem = Vue.util.extend({}, license);
newItem.expires_at = moment(license.expires_at).format('MM/DD/YYYY');
return newItem;
});
}
},
components: {
myUpdater: {
props: ['items'],
methods: {
doUpdate(event, index) {
const newObj = this.items.map(item => Vue.util.extend({}, item));
newObj[index].expires_at = new Date(event.target.value);
console.log("new object", newObj);
this.$emit('update:items', newObj);
}
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<my-updater :items.sync="licenseItems" inline-template>
<div>
<input v-for="item, index in items" :value="item.expires_at" #change="doUpdate($event, index)">
</div>
</my-updater>
<div v-for="item in items">
{{item.expires_at}}
</div>
</div>