Issue when trying to interact with an API in Vuejs? - javascript

datalist.js
import axios from "axios";
export const datalist = () => {
return axios.get("myapiurl/name...").then((response) => response);
};
HelloWorld.vue
<template>
<div>
<div v-for="item in items" :key="item.DttID">
<router-link
:to="{
name: 'UserWithID',
params: { id: item.DepaD },
query: { DepaD: item.DepaID },
}"
>
<div class="bt-color">{{ item.DepaName }}</div>
</router-link>
</div>
<br /><br /><br />
<User />
</div>
</template>
<script>
import User from "./User.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
},
data() {
return {
items: datalist,
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
};
</script>
User.vue
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item.Accv }}
</div>
</div>
</template>
<script>
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
lists: datalist,
};
},
computed: {
user: function () {
return this.lists.filter((item) => {
if (item.DepaD === this.$route.params.id) {
return item;
}
});
},
},
};
</script>
Error with the code is,
[Vue warn]: Error in render: "TypeError: this.lists.filter is not a function"
TypeError: this.lists.filter is not a function
The above error i am getting in User.vue component in the line number '20'
From the api which is in, datalist.js file, i think i am not fetching data correctly. or in the list filter there is problem in User.vue?

Try to change the following
HelloWorld.vue
data() {
return {
items: [],
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
User.vue
data() {
return {
lists: []
};
},
mounted() {
datalist().then((r) => {
this.lists = r.data;
});
},
At least this suppress the error, but i cant tell more based on your snippet since there are network issues :)

Since your datalist function returns a Promise, you need to wait for it to complete. To do this, simply modify your component code as follows:
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
// empty array on initialization
lists: [],
};
},
computed: {
user: function() {
return this.lists.filter((item) => {
if (item.DeploymentID === this.$route.params.id) {
return item;
}
});
},
},
// asynchronous function - because internally we are waiting for datalist() to complete
async-mounted() {
this.users = await datalist() // or datalist().then(res => this.users = res) - then async is not needed
}
};
now there will be no errors when initializing the component, since initially lists is an empty array but after executing the request it will turn into what you need.

You may define any functions and import them, but they wont affect until you call them, in this case we have datalist function imported in both HelloWorld and User component, but it did not been called in User component. so your code:
data() {
return {
lists: datalist,
};
},
cause lists to be equal to datalist that is a function, no an array! where .filter() should be used after an array, not a function! that is the reason of error.
thus you should call function datalist and put it's response in lists instead of putting datalist itself in lists
Extra:
it is better to call axios inside the component, in mounted, created or ...
it is not good idea to call an axios command twice, can call it in HelloWorl component and pass it to User component via props

Related

Vue not reacting to a computed props change

I am using the Vue composition API in one of my components and am having some trouble getting a component to show the correct rendered value from a computed prop change. It seems that if I feed the prop directly into the components render it reacts as it should but when I pass it through a computed property it does not.
I am not sure why this is as I would have expected it to be reactive in the computed property too?
Here is my code:
App.vue
<template>
<div id="app">
<Tester :testNumber="testNumber" />
</div>
</template>
<script>
import Tester from "./components/Tester";
export default {
name: "App",
components: {
Tester,
},
data() {
return {
testNumber: 1,
};
},
mounted() {
setTimeout(() => {
this.testNumber = 2;
}, 2000);
},
};
</script>
Tester.vue
<template>
<div>
<p>Here is the number straight from the props: {{ testNumber }}</p>
<p>
Here is the number when it goes through computed (does not update):
{{ testNumberComputed }}
</p>
</div>
</template>
<script>
import { computed } from "#vue/composition-api";
export default {
props: {
testNumber: {
type: Number,
required: true,
},
},
setup({ testNumber }) {
return {
testNumberComputed: computed(() => {
return testNumber;
}),
};
},
};
</script>
Here is a working codesandbox:
https://codesandbox.io/s/vue-composition-api-example-forked-l4xpo?file=/src/components/Tester.vue
I know I could use a watcher but I would like to avoid that if I can as it's cleaner the current way I have it
Don't destruct the prop in order to keep its reactivity setup({ testNumber }) :
setup(props) {
return {
testNumberComputed: computed(() => {
return props.testNumber;
}),
};
}

Try to pass $ref data to prop data of component

Hello I have a component Carousel, and I have method:
carousel () {
return this.$refs.carousel
}
When I try pass to prop of other component, I get undefined in prop data, why?
<Dots :carousel="carousel()" />
In child component I have:
export default {
name: 'Dots',
props: ['carousel']
}
In mounted when I try call console.log(this.carousel), I get undifined. But I need get a component data of Carousel. How I can do it?
Sandbox: https://codesandbox.io/s/flamboyant-monad-5bhjz?file=/src/components/Test.vue
You can set a function in carousel to return the data you want to send to dots. Then, in the parent component, set its return value to an attribute in data which will be passed as a prop:
const dotsComponent = Vue.component('dotsComponent', {
template: '#dotsComponent',
props: ['carousel']
});
const carouselComponent = Vue.component('carouselComponent', {
template: '#carouselComponent',
data() { return { id:1 } },
methods: {
getData() { return { id: this.id } }
}
});
new Vue({
el: "#app",
components: { dotsComponent, carouselComponent },
data() { return { carousel:null } },
mounted() { this.carousel = this.$refs.carousel.getData(); }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div><carousel-component ref="carousel"/></div>
<div><dots-component :carousel="carousel"/></div>
</div>
<template id="dotsComponent"><p>Dots: {{carousel}}</p></template>
<template id="carouselComponent"><p>Carousel</p></template>
You could pass the ref directly without using a method any other property :
<Dots :carousel="$refs.carousel" />

Make a reactive component with vuejs

I need a Vue component to show some HTML content in v-data-table from Vuetify. I have seen this post Vue 2 contentEditable with v-model, and I created a similar code shown below.
My problem is the component is not reactive. When I click the "Test button", no content is updated in HtmlTextArea.
<template>
<div>
<v-btn #click="doTest()">Test Button</v-btn>
<HtmlTextArea
v-model="content"
style="max-height:50px;overflow-y: scroll;"
></HtmlTextArea>
</div>
<template>
export default {
name: "ModelosAtestados",
components: { HtmlTextArea },
data: () => ({
content: "",
}),
methods: {
doTest() {
this.content = "kjsadlkjkasfdkjdsjkl";
},
},
};
//component
<template>
<div ref="editable" contenteditable="false" v-on="listeners"></div>
</template>
<script>
export default {
name: "HtmlTextArea",
props: {
value: {
type: String,
default: "",
},
},
computed: {
listeners() {
return { ...this.$listeners, input: this.onInput };
},
},
mounted() {
this.$refs.editable.innerHTML = this.value;
},
methods: {
onInput(e) {
this.$emit("input", e.target.innerHTML);
},
},
};
</script>
This occurs because HtmlTextArea sets the div contents to its value prop only in the mounted lifecycle hook, which is not reactive.
The fix is to setup a watcher on value, so that the div contents are updated to match whenever a change occurs:
// HtmlTextArea.vue
export default {
watch: {
value: {
handler(value) {
this.$refs.editable.innerHTML = value;
}
}
}
}
demo
In the #click event binder, you have to pass a function. You passed the result of an executed function.
To make it work: #click="doTest" or #click="() => doTest()".
How to debug such problems:
Display the value you want to update on your template to check if its updated: {{content}}
Use the vue devtool extension to check the current state of your components

VueJS App put data from GET request inside an array

This is my script of a VueJS component which should show data inside a dashboard.
<script>
import { Chart } from "highcharts-vue";
import Highcharts from "highcharts";
import exportingInit from "highcharts/modules/exporting";
//import stockInit from "highcharts/modules/stock";
//stockInit(Highcharts);
exportingInit(Highcharts);
export default {
props: {
partsdata: {
type: Array
}
},
methods: {
fetchData() {
this.$http.get('http://127.0.0.1:5000/data/')
.then(response => {
return response.json();
}).then(data => {
var result = JSON.parse(data) // eslint-disable-line no-console
console.log(result.Fare) // eslint-disable-line no-console
})
}
},
components: {
highcharts: Chart
},
data() {
return {
chartOptions: {
series: [{
data: [] // sample data
}]
}
}
}
};
</script>
Receiving the data from my backend works fine, but I am unable to get it into the chart. I still struggle how to work with async tasks, so I tried this and failed
this.data.push(result.Fare)
Header.vue?5e07:33 Uncaught (in promise) TypeError: Cannot read
property 'push' of undefined
at VueComponent.eval (Header.vue?5e07:34)
Can anyone tell me how to do this?
Update: This is my template where I trigger the Method:
<template>
<div>
<button #click="fetchData" class="btn-primary">Get Data</button>
<highcharts
:options="chartOptions"
></highcharts>
</div>
</template>
Looking at your code if you're trying this.data.push its understandable that its telling you its undefined.
To get to your data prop in your fetchData method youd have to use
this.chartOptions.series[0].data.push
I'm not sure where you're firing fetchData either in that code. Try move it to your created/mounted methods in the vue lifecycle, you can then change your chartOptions to a computed or watcher, setting data in the data() method and calling it in your chartOptions with this.data.
UPDATE:
On click is fine for your get request if it's firing keep that the same, its where you're then setting it thats the issue.
I refactored your request, if its successful when you test it response.data should give you the results you need, not sure why you need to parse it unless its returning a json blob as a string?
I also added a v-if to your chart renderer, if this.data is empty then don't render. See how you get on with that.
import { Chart } from "highcharts-vue";
import Highcharts from "highcharts";
import exportingInit from "highcharts/modules/exporting";
//import stockInit from "highcharts/modules/stock";
//stockInit(Highcharts);
exportingInit(Highcharts);
export default {
props: {
partsdata: {
type: Array
}
},
methods: {
fetchData() {
this.$http.get('http://127.0.0.1:5000/data/')
.then(response => {
this.data.push(JSON.parse(response.data))
})
}
},
watch: {
data: function() {
this.chartOptions.series[0].data.push(this.data)
}
},
components: {
highcharts: Chart
},
data() {
return {
data: [],
chartOptions: {
series: [{
data: [] // sample data
}]
}
}
}
};
</script>
<template>
<div>
<button #click="fetchData" class="btn-primary">Get Data</button>
<highcharts if="data.length > 0" :options="chartOptions" ></highcharts>
</div>
</template>
You need to correctly get data and change chartOptions:
<template>
<div>
<highcharts class="hc" :options="chartOptions" ref="chart"></highcharts>
<button #click="fetchData" class="btn-primary">Get Data</button>
</div>
</template>
<script>
export default {
methods: {
fetchData() {
fetch('https://api.myjson.com/bins/ztyb0')
.then(response => {
return response.json()
})
.then(data => {
this.chartOptions.series[0].data = data;
})
}
},
data() {
return {
chartOptions: {
series: [{
data: []
}]
}
};
}
};
</script>
Live demo: https://codesandbox.io/s/highcharts-vue-demo-05mpo
Docs: https://www.npmjs.com/package/highcharts-vue

Access to props inside javascript

I just start to learn vuejs 2 but I have an issue, I'm passing a props to a child component like this :
<div class="user">
<h3>{{ user.name }}</h3>
<depenses :user-id="user.id"></depenses>
</div>
And here is how I use it in my child component :
export default {
name: 'depenses',
props: ['userId'],
data () {
return {
depenses: []
}
},
mounted: function () {
this.getDepenses()
},
methods: {
getDepenses: function () {
this.$http.get('myurl' + this.userId).then(response => {
this.depenses = response.body
this.haveDepenses = true
}, response => {
console.log('Error with getDepenses')
})
}
}
}
this.userId is undefined but I'm able to display it with <span>{{ userId }}</span> and I can see in the vuejs console the param userId with the value
Why I have an undefined value in the js ?
Ok I found why I couldn't get my value, thanks to #Saurabh comment, he reminds me that I get my props from an async way, so I had to set a watcher when my props change, like this :
watch: {
userId: function () {
this.getDepenses()
}
},

Categories

Resources