vuejs displaying API data using attribute binding - javascript

I have a simple app where a user selects a Person and vue makes an api call for that users posts.Each of these posts in turn have their own comments.This is all from https://jsonplaceholder.typicode.com/
The comment section is always empty.
The codepen is here
My html
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id='app'>
<div v-if='isError'>
There was an error
</div>
<div v-else-if='isLoading'>
Loading
</div>
<div v-else>
<select v-model="selectedUser">
<option v-for="person in info" v-bind:value="person"> {{person.name}}</option>
</select>
</div>
<div class="posts">
<div v-if="isLoadingPosts">
Loading...
</div>
<div v-else>
<ul>
<li v-for="post in postData">
<p>
{{ post.body }}
</p>
<button v-bind:id='post.id' v-on:click='getComments'>
View Comments
</button>
</li>
</ul>
</div>
</div>
<div class="comments">
<p>
{{ commentData }}
</p>
</div>
</div>
JS logic
var app = new Vue({
el: '#app',
data: {
info : null,
isLoading : true,
isError : false,
selectedUser : '',
postData : null,
isLoadingPosts : true,
commentData : null,
},
watch : {
selectedUser : function () {
axios
.get('https://jsonplaceholder.typicode.com/posts?userId=' + this.selectedUser.id)
.then(response => (this.postData =response.data))
.catch(error=> {console.log(error)})
.finally(() => this.isLoadingPosts = false)
}
},
methods : {
getComments : function (){
axios
.get('https://jsonplaceholder.typicode.com/posts/' + this.id + '/comments')
.then(response => (this.commentData =response.data))
}
},
mounted () {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then(response => (this.info = response.data))
.catch(error => {console.log(error);this.isError = true})
.finally(() => this.isLoading = false)
}
})
Everything works except the comments part where it always returns an empty object.I also feel that my code is repetitive,any corrections would be appreciated.

So you have a couple issues with this method:
getComments : function (){
axios
.get('https://jsonplaceholder.typicode.com/posts/' + this.id + '/comments')
.then(response => (this.commentData =response.data))
}
}
First, this.id in there will be looking for an id prop on the component itself, not the id you're trying to bind in your button.
Try changing the button code to this:
<button v-on:click='getComments(post.id)'>View Comments</button>
And then the method to:
getComments : function (id){
axios
.get('https://jsonplaceholder.typicode.com/posts/' + id + '/comments')
.then(response => (this.commentData =response.data))
}
}
Also, you might want to add a .catch() handler like you id for your other axios calls.

Related

Vue - Get id of particular clicked post

Basically my goal is getting the number of click for each post.
Until now, i only just successfully get the number per clicks on console :
But i need to know which post was clicked, For distinct that, i want to display the id of post, so i supposed each post have some: _id
That is my post content from my DB :
My code :
<template>
<div w="3" v-for="(post, post_id) in posts" :key="post_id">
<div class="card-view" #click="getStat(post_id)">
<div class="container">
<div class="tag_name blue">{{ post.title }}</div>
<div class="company">{{ post.body }}</div>
<div class="c_logo">
<img :src="`${post.image}`"/>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "Home",
components: {
modal,
},
data: () => ({
posts: [],
}),
methods : {
getStat : function() {
let increment = this.counter += 1
console.log(increment)
let result = axios.post("http://localhost:3000/allPostClicked", {
numberOfClick: increment,
postID: this.post_id
})
console.log(result)
}
},
},
</script>
...and when i clicked on some post, i got this error on my console :
[[Prototype]]
:
Promise
[[PromiseState]]
:
"rejected"
[[PromiseResult]]
:
Error: Request failed with status code 404 at createError (webpack-internal:///./node_modules/axios/lib/core/createError.js:16:15) at settle (webpack-internal:///./node_modules/axios/lib/core/settle.js:17:12) at XMLHttpRequest.onloadend (webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:54:7)
config
:
{url: 'http://localhost:3000/allPostClicked', method: 'post', data: '{"numberOfClick":null}', headers: {…}, transformRequest: Array(1), …}
isAxiosError
:
true
request
...
If I understood you correctly take a look at following snippet
const app = Vue.createApp({
data: () => ({
posts: [],
}),
async mounted() {
await this.getPosts()
this.posts = this.posts.map(p => {
return {...p, numberOfClick: 0}
})
},
methods : {
async getPosts() {
await fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json => this.posts = json)
},
getStat(id) {
const post = this.posts.find(c => c.id === id)
post.numberOfClick++
},
},
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div w="3" v-for="(post, i) in posts" :key="i">
<div class="card-view" #click="getStat(post.id)">
<div class="container">
<h5 class="tag_name blue">{{ post.title }}</h5>
<div class="company">{{ post.body }}</div>
<h3>{{ post.numberOfClick }}</h3>
</div>
</div>
</div>
</div>

How can I render an axios response in v-if ? Is there an other way?

It's my first time with Vue.js and I think I missed some concepts despite reading lots of doc !
I'm trying to display a glyphicon if a call to my api return true.
But it's called in a for loop for each ittération, so I can't store the response into a var as each call will overwrite the previous one...
the code where I want to do the display is here :
<div class="list-group-item"
v-for="item in exercices['data']" :key="item">
<h3 class="list-group-item-heading titre">
<p><a > {{ item[1] }}</a>
<span v-if="checkIsValid(item[0]) === true">
<span class="glyphicon glyphicon-ok" ></span>
</span>
</p>
And axios is called here :
checkIsValid(id, index){
this.$axios
.get('http://127.0.0.1:5000/checkIsValid/'+id+'/'+ Vue.prototype.$userMail)
.then(response => ( ??? )
)
}
Do you have any ideas on how I should do this ?
Thanks in advance !
Your method checkIsValid performs an async call, so you cannot use it directly in a v-if. I would need to know more about your data object to respond properly but you could check each of your exercices elements in the mounted hook and store the result in each object.
export default {
// ...
data() {
return {
exercices: [
{
// ...
},
{
// ...
}
]
}
},
mounted() {
this.exercices.forEach(element => {
this.$axios.get("URL_HERE")
.then(() => {
this.$set(element, "isValid", true);
})
.catch(() => {
this.$set(element, "isValid", false);
})
});
}
}
Note that this.$set is required. See more about change detection caveats here.
Then change your v-if to:
<span v-if="item.isValid">

Why is my false conditional element fickering while route is loading?

Background: I have a page that displays a different div depending on Axios response. If the Axios Get request brings back response that does not populate data array myExpense[], it shows the div with the alert-warning class. Other wise if the response does populate the array it shows the div with the alert-succes class.
Here is the code:
<template>
<div class="profilePage">
<div class="container">
<div class="row">
<div class="col" v-if="Array.isArray(myExpense)
&& myExpense == 0"><!--Warning div-->
<p class="alert alert-warning">You do not have a budget at the moment</p>
<router-link to="/budgetform" tag="button" class="btn btn-primary mt-5">Create Budget</router-link>
</div>
<div v-else class="col-md-12 alert alert-success"><!--Success div-->
<p class="yourBalance">
Your monthly balance
<br />
<span>${{myExpense[0].earnings}}</span>
</p>
<router-link to="/mybudget" tag="button" class="btn btn-primary mt-5">My budget</router-link>
</div>
</div>
<div class="row">
<div class="col-md-12">
Logout
</div>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "userProfile",
data() {
return {
myExpense: [],//This array to be populated by axios response
};
},
methods: {},
created() {
axios
.get("http://localhost:3000/budget", {
headers: { "Content-Type": "application/json" },
withCredentials: true
})
.then(res => {
if (res.data.name == undefined) {
this.$router.push("/");
}
this.profile = res.data;
this.myExpense = res.data.budget;
});
}
};
</script>
Problem:
Whenever this page is rendered, the false div flickers before displaying the right div based on the condition. Also, sometimes it shows white space as well.
I tried adding v-cloak but that doesn't seem to be the problem.
Thanks in advance for the help
I was able to find out the problem. The get request was not retrieving the data as fast as it took to render the page. So I had to use beforeRouteEnter to the retrieve the data before the component renders
Like so:
beforeRouteEnter(to, from, next) {
axios
.get("http://localhost:3000/budget", {
headers: { "Content-Type": "application/json" },
withCredentials: true
})
.then(res => {
next(vm => {
vm.userName = res.data.name;
vm.myExpenses = res.data.budget[0].expenses;
vm.earnings = res.data.budget[0].earnings;
});
})
.catch(err => {
next(vm => {
vm.err = err;
});
});
}

Passing a form value to build a Rest API url in Vue.js

I created this component in vue :
<template>
<div>
<h2>Find User By ID</h2>
<input v-model="userId" type="number" placeholder="modifiez-moi" />
<br />
<p v-if="userId.length != 0">
L'Utilisateur est : {{ getUser(userId) }}
<!-- returns nothing...-->
<br />
{{ user["id"] }} {{ user["email"] }} {{ user["username"]
}}<!-- returns object-->
</p>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "FindUser",
data() {
return {
user: null,
userId: ""
};
},
methods: {
getUser(userId) {
axios
.get("http://localhost:4000/api/users/" + this.userId)
.then(response => {
console.log(response);
this.user = response.data.data;
});
}
},
mounted() {
// this.getUser();
}
};
</script>
This code is working... But i have several issues :
if i type an id that not fit a user, the previous result is not removed
Ideally i'd like to use a button to send the request
My third issue is that the request is repeated for exemple, the console.log is displayed more than one time...
the console is executing different time because on top of your code you are using if statement if userId.length !=0 then on each key enter it will send request.please don't use this if statement
Button to send the request is a good idea, use that.(other
options could be keyup event, debounce if a button is too much).
Change the v-if to something more concrete like test for
user !== null since user is default as null.
Check for a validation for a number input only when calling the
method from the button that makes sure that you intended the input to
be number and api is called only with a valid input.
Can try this:
<template>
<div>
<h2>Find User By ID</h2>
<input v-model="userId" type="number" placeholder="modifiez-moi" />
<!--#click is shorthand for v-on:click-->
<button #click="getUser($event)">Get User</button>
<br />
<p v-if="user !== null">
L'Utilisateur est : {{ displayWhateverHere }}
<br />
{{ user["id"] }} {{ user["email"] }} {{ user["username"] }}
</p>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "FindUser",
data() {
return {
user: null,
userId: ""
};
},
methods: {
getUser(event) {
/*Check for validations on input and then call the api*/
let validFlag = !isNaN(this.userId);
if (validFlag) {
axios
.get("http://localhost:4000/api/users/" + this.userId)
.then(response => {
console.log(response);
this.user = response.data.data;
/*If needed to clear the userId*/
this.userId = "";
});
} else {
/*input value is not a number -do something about it (alert/notify)*/
this.userId = "";
}
}
},
mounted() {
// this.getUser();
}
};
</script>

Error while binding array to state

I am trying to use fetch to get json data from my back end and then put it on a array, and show it on the screen, for now on the console log.
I am trying to store the information I get back in a array called data which I initialized in getinistate, and then put json data in it while the fetch call is done. For now The error I am reciving is that console.log is basically empty.
Here is the code.
<body>
<div id="reactBinding"></div>
<script type="text/babel">
var Heading = React.createClass({
getInitialState: function() {
return {
data: [],
amount : 1000
};
},
handleChange: function(event){
this.setState({amount : event.target.value});
},
loadCommentsFromServer: function() {
var value = {
method : 'GET' ,
headers : {
'Accept': 'application/json',
'contentType' : 'application/x-www-form-urlencoded',
},
body : ({
amount : this.state.amount
})
};
fetch('http://localhost:3000/getIOT', value)
.then((response) => response.json())
.then((responseData) =>{
responseData : this.state.data
})
.catch(function(err){
console.log(err);
});
},
showTable : function(){
console.log(data);
},
render : function(){
var amount = this.state.amount;
return(
<div className="container">
<div className="row">
<div classname="col-xs-4 col-xs-offset-4">
<div className="text-center">
<h1>{this.props.name}</h1>
<h2> {amount} </h2>
<input type="text" value={amount} onChange={this.handleChange} />
<button onClick={this.showTable}>Show Table</button>
<button onClick={this.loadCommentsFromServer}> Submit </button>
</div>
</div>
</div>
</div>
);
}
});
ReactDOM.render(
<div>
<Heading
name="React JS"
>
</Heading>
</div>
, document.getElementById('reactBinding'));
</script>
</body>
So again, what I want to do is get the information from fetch, put it in the variable called data array and then when someone clicks showTable, it should console.log the array out. Totally new to react so need a bit of handholding since this is the first time I am writing it. If this code is a bit too messy it would be great someone could help show me how to write a simple fetch.
Also if you have time it would be great if someone could explain how can I display the array in a table. in the showTable part.
You need to use the setState to store the data in state variable once you get the response, like this:
fetch('http://localhost:3000/getIOT', value)
.then((response) => response.json())
.then((responseData) =>{
//responseData : this.state.data
this.setState({data: responseData}); // use this line
})
put the console.log in render function, it will print the data once you get the response, like this:
render : function(){
var amount = this.state.amount;
console.log('data', this.state.data);
....
Update:
Check the working Code:
var Heading = React.createClass({
getInitialState: function() {
return {
data: [],
amount : 1000
};
},
handleChange: function(event){
this.setState({amount : event.target.value});
},
loadCommentsFromServer: function() {
var value = {
method : 'GET' ,
headers : {
'Accept': 'application/json',
'contentType' : 'application/x-www-form-urlencoded',
},
body : ({
amount : this.state.amount
})
};
fetch('http://localhost:3000/getIOT', value)
.then((response) => response.json())
.then((responseData) =>{
this.setState({data: responseData});
})
.catch(function(err){
console.log(err);
});
},
showTable : function(){
console.log(this.state.data);
},
render : function(){
var amount = this.state.amount;
console.log('data', this.state.data);
return(
<div className="container">
<div className="row">
<div classname="col-xs-4 col-xs-offset-4">
<div className="text-center">
<h1>{this.props.name}</h1>
<h2> {amount} </h2>
<input type="text" value={amount} onChange={this.handleChange} />
<button onClick={this.showTable}>Show Table</button>
<button onClick={this.loadCommentsFromServer}> Submit </button>
</div>
</div>
</div>
</div>
);
}
});
ReactDOM.render(
<div>
<Heading
name="React JS"
>
</Heading>
</div>
, document.getElementById('reactBinding'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='reactBinding'></div>

Categories

Resources