Vue js computed result not accurate - javascript

I am trying to implement a vote button with vue js, when user click "Vote" will send axios request to server and store the data, then return json back. Same with unvote.
I also check if the user is voted, the button should change to Unvote like facebook.
So far the vote and unvote button is work correctly.
But i found a problems which is the voted features is not working. If user voted, after refresh page it will change back to "Vote", but it should be Unvote. But if the button was clicked, in database will showing the vote was deleted. Mean it should be problems of computed. But i am struggle on it since i not really know vue js.
This is my vue components.
<template>
<a href="#" v-if="isLiked" #click.prevent="unlike(comment)">
<span>UnVote</span>
</a>
<a href="#" v-else #click.prevent="like(comment)">
<span>Vote</span>
</a>
<script>
export default{
props: ['comment','liked'],
data: function() {
return {
isLiked: '',
}
},
mounted() {
axios.get('/comment/'+ this.comment.id +'/check', {})
.then((response) => {
this.liked = response.data;//here will get json "true" or "false"
});
this.isLiked = this.liked ? true : false;
},
computed: {
isLike() {
return this.liked;
},
},
methods: {
like(comment) {
axios.post('/comment/'+ comment.id +'/like')
.then(response => this.isLiked = true)
.catch(response => console.log(response.data));
},
unlike(comment) {
axios.post('/comment/'+ comment.id +'/unlike')
.then(response => this.isLiked = false)
.catch(response => console.log(response.data));
},
}
}

Your component instance does not have a liked data property and you should not attempt to set prop values (see https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow)
Also, you are attempting to set the isLiked value outside of the asynchronous operation which will not work how you think.
Just set the isLiked property...
mounted() {
axios.get('/comment/'+ this.comment.id +'/check', {})
.then((response) => {
this.isLiked = response.data; //here will get json "true" or "false"
});
},
Your isLike computed property is also never used.

Related

"Write operation failed: computed property "posts" is readonly"

I try remove post from PostList, but getting an error
<PostList
:posts="sortedAndSearchedPosts"
#remove="removePost"
v-if="!isPostsLoading"
/>
remove post function
removePost(post) {
this.posts = this.posts.filter((p) => p.id !== post.id);
},
posts initialized here
computed: {
...mapState({
posts: (state) => state.post.posts,
}),
...mapGetters({
sortedPosts: "post/sortedPosts",
sortedAndSearchedPosts: "post/sortedAndSearchedPosts",
}),
},
Not clear where this.posts are actually initially set up.
If I guess your architecture right, I see 2 options for you:
either directly remove the post in your store so that sortedAndSearchedPosts reflects the change directly
if you don't want to update the global state, build a local copy when you initialise posts:
data () {
return {
posts: [...this.sortedAndSearchedPosts],
...
}
},
and update your code to use the local list of posts:
<PostList
:posts="posts"
...
/>
Hope this helps!

Why local storage not persisting after page refresh

The button text 'completed' should persist after browser refresh based on whether var item is true (after button click). I am not sure what the issue is but I have tried Chrome as well so I don't think it is browser related.
<template>
<button type="button" v-bind:class="order_button_style" #click="on_order_button_click()">
{{ buttonText }}
</button>
</div>
</template>
<script>
export default {
item: '',
data() {
return {
item2: this.item
}
},
methods: {
on_order_button_click() {
this.item2 = true;
localStorage.setItem(this.item2);
}
},
mounted() {
const storedState = localStorage.getItem(this.item2) === 'false';
if (storedState) {
this.item2 = storedState;
}
},
computed: {
buttonText() {
return this.item2 === true ? "Completed" : "Complete";
},
order_button_style() {
return this.item2 === true
? "btn btn-danger"
: "btn btn-primary";
}
}
};
</script>
localStorage.setItem takes 2 params, name and value.
I believe you meant to write the following:
Setting an item in localStorage
localStorage.setItem('item', this.item2)
and retrieving it
localStorage.getItem('item')
A few comments on other parts of your code:
this.item2 === true can be shortened to this.item2 if item2 can never be anything other than a boolean.
You're currently only using the value from localStorage if it's false, which it will never be because you're only ever calling setItem with a value of true
I'm not sure what you're trying to do with the item prop in the top level of the object
Strongly consider using camelCase for your method names. Follow global conventions
UPDATE:
I think this is what you're trying to achieve:
on_order_button_click() { // Should probably rename this to `onOrderButtonClick`, the current method name hurts to look at
this.clickedOrderButton = true;
localStorage.setItem('clickedOrderButton', this.clickedOrderButton);
}
created() {
this.clickedOrderButton = localStorage.getItem('clickedOrderButton') === "true";
}
I've renamed item2 to clickedOrderButton. I have no idea why you'd name that variable item2 based on the code that is shown.
There's also no need to check whether clickedOrderButton is true before assigning it to clickedOrderButton, as it will resolve to false if it's not present (or intentionally set to something other than true) in localStorage.
Lastly, I've replaced mounted by created. As you're not accessing any DOM elements, there's no need to wait until the component is mounted to run that code
UPDATE#2:
If you have several instances of this component, you'll need to set use a different name than clickedOrderButton. You can use a unique identifier per button, which you can pass as a prop from above.
E.g.
props: {
id: { type: String, required: true }
}
...
localStorage.setItem(`clickedOrderButton-${this.id}`, this.clickedOrderButton);
...
localStorage.getItem(`clickedOrderButton-${this.id}`);

getting a restful Api using the fetch() in Vuex

im really going through hard times trying to figure out how to get my API data through Vuex, is there some body whom has accurate bibliography of how to do this step by step, or even better help me with this code?
Formerly without using Vuex , but Vue all request worked perfectly, but now i dont understand clearly what i should do, here sharing part of my code:
data() {
return {
testArray: []
};
methods: {
getJsonData() {
fetch(
"https://app.ticketmaster.com/discovery/v2/events.json?countryCode=" +
this.countriesDrop +
"&apikey=xxxxxxxxxxxxxxxxxxxxxxxx",
{
method: "GET"
}
)
.then(response => {
return response.json();
})
.then(test => {console.log(this.testArray)
this.testArray = test._embedded.events;
})
.catch(err => {
console.log(err);
});
},
watch: {
countriesDrop: function(val) {
this.getJsonData();
}
},
As you can see in the request also is included an external element which make it changes attuning with the watcher and the value the user might asign.
I already got set Vuex and all else pluggins...just dont know how to act like , thus would appreciate an accurate link or tutorial either help with this basic problem resolved on detail step by step, .....thanks!
In your code there's nothing with Vuex. I guessed you want to set the state so that the getJsonData() method is called according to what's in the store.
Here's a snippet as an example of handling async in a Vuex environment.
const store = new Vuex.Store({
state: {
testArray: []
},
mutations: {
setTestArray(state, data) {
state.testArray = data
}
},
actions: {
getJsonData({
commit
}, countriesDrop) {
if (countriesDrop && countriesDrop !== '') {
fetch(`https://jsonplaceholder.typicode.com/${countriesDrop}`, {
method: "GET"
})
.then(response => {
return response.json();
})
.then(json => {
commit('setTestArray', json)
})
.catch(err => {
console.log(err);
});
}
}
}
})
new Vue({
el: "#app",
store,
computed: {
getDataFromStore() {
return this.$store.state.testArray
}
},
methods: {
getData(countriesDrop) {
this.$store.dispatch('getJsonData', countriesDrop)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/es6-promise#4/dist/es6-promise.auto.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="getData('todos')">GET TODOS</button>
<button #click="getData('albums')">GET ALBUMS</button>
<ol>
<li v-for="data in getDataFromStore">{{data.title}}</li>
</ol>
</div>
The point is that Vuex is a central element in a Vue-Vuex application. You can store app state, handle async and sync functions (actions, mutations) with it, and all your Vue components can rely on the state - that should be the "single source of truth".
So, you get your input from a component (the Vue instance in this snippet), and dispatch an action that is available in the Vuex store. If the action needs to modify the state, then you call a mutation to do that. With this flow you keep reactivity for all your components that use that state.
I used a computed to get data from the Vuex store, but getters can be set also.
This way you don't "pollute" your components with functions and data that should be in the store.

VueJs + Laravel - like button component

I'm trying to get a good understanding of VueJS, and I'm using it with Laravel 5.7 for a personal project, but I can't exactly figure out how to do a, probably, simple task a "like" button\icon.
So, here's the situation, I have a page, displaying various posts from my database, and at the bottom of each post I want a "like toogle" button, which I made with an icon followed by the number of likes on that post; At first the button will contain the data retrieved from the corresponding database table, but if you click it will increase the displayed number by one and insert a new like in the database.
I made the "like" icon as a component :
<section class="bottomInfo">
<p>
<likes now="{{ $article->likes }}"></likes>
<span class="spacer"></span>
<span class="entypo-chat">
{{ $article->comments }}
</p>
</section> <!-- end .bottomInfo -->
As you can see there's a <likes> in which I added a prop now, by what I'm understanding till now about components, in this way I can insert the data from my db as a starting value (now contains the db row value), problem is, I don't know where\how to keep that value in my app, in which I'm gonna also use axios for increasing the likes.
Here's the component:
<template>
<span class="entypo-heart"> {{ now }}</span>
</template>
<script>
export default {
props: ['now'],
data() {
return {
like: this.now
}
},
mounted() {
console.log('Component mounted.');
}
}
</script>
What I tried to do (and I don't know if it's correct) is to pass the value of now to the data function inside a property named like, so, if I understood correctly, that variable like is now part of my properties in my main Vue instance, which is this one
const app = new Vue({
el: '#main',
mounted () {
console.log("The value of 'like' property is " + this.like)
},
methods: {
toggleLike: function() {
} //end toggleLike
}
});
The mounted function should print that property value, but instead I get
The value of 'like' property is undefined
Why? Is this how it works? How can I make it so I can get that value and also update it if clicked, to then do a request to my API? (I mean, I'm not asking how to do those single tasks, just where\how to implement it in this situation). Am i getting the component logic right?
Probably a bit more verbosity never hurt:
props: {
now: {
type: Number,
required: true
}
}
Instead of using the data function, use a computed property:
computed: {
likes: {
get: function() {
return this.now
}
}
}
However, here comes the problem.
If you need to change the # of likes after the user clicks like, you have to update this.now. But you can't! It's a property, and properties are pure. Vue will complain about mutating a property
So now you can introduce a data variable to determine if the user has clicked that like button:
data() {
return {
liked: 0
}
}
Now we can update our computed property:
likes: {
get: function() {
return this.now + this.liked
}
}
However, what are we liking? Now we need another property:
props: {
id: {
type: Number,
required: true
},
now: {
type: Number,
required: true
}
}
And we add a method:
methods: {
add: function() {
//axios?
axios.post(`/api/articles/${this.id}/like`)
.then (response => {
// now we can update our `liked` proper
this.liked = 1
}).catch(error => {
// handle errors if you need to
)}
}
}
And, let's make sure clicking our heart fires that event:
<span class="entypo-heart" #click="add"> {{ now }}</span>
Finally our likes component requires an id property from our article:
<likes now="{{ $article->likes }}" id="{{ $article->id }}"></likes>
With all this in place; you're a wizard now, Harry.
Edit
It should be noted that a user will be forever able to like this, over and over again. So you need some checks in the click function to determine if they like it. You also need a new prop or computed property to determine if it was already liked. This isn't the full monty yet.

VueJS throws errors because some datas are not ready yet

I'm rather new to VueJS, and I would like to add a picture loaded from an API as a background image. However, the image loading eventually works.
Here is the code:
<template>
<div id="bio" :style="{ backgroundImage: 'url(' + this.settings.bio_bg.url +')' }">
<h1>Biography</h1>
<router-link to="/">Home</router-link><br />
<span>Biography</span><br />
<router-link to="/shop">Shop</router-link><br />
<router-link to="/contact">Contact</router-link><br />
</div>
</template>
<style scoped>
</style>
<script>
export default {
data () {
return {
settings: {},
bio: {}
}
},
created () {
.catch(error => this.setError(error))
this.$http.secured.get('/settings')
.then(response => {
this.settings = response.data
console.log(this.settings)
})
.catch(error => this.setError(error))
}
}
</script>
The image is loaded, but my console returns two errors:
Error in render: "TypeError: Cannot read property 'url' of undefined"
Cannot read property 'url' of undefined
I guess that since the Axios call is asynchronous, everything arrives after the page is done loading, but is still loaded after.
What would the proper way be to correctly wait for data to be available? I tried a few things that I know from React, but it doesn't load at all (even though the errors stop showing up)
Thank you in advance
Yo need to be sure that this.settings.bio_bg.url exist from the component birth, so compiler doesn't broke trying to render it. In order to do so, just setup a 'fake' url in the original data of the component:
export default {
data () {
return {
settings: {
bio_bg: {
url: '',
}
},
bio: {}
}
},
created () {
this.$http.secured.get('/settings')
.then(response => {
this.settings = response.data
console.log(this.settings)
})
.catch(error => this.setError(error))
}
}
This not only prevent errors, also provides better intellisense since now your code editor can now that settings has a bio_bg member which in turn has an url.
If instead of ' ', you provide a real image url with a placeholder img, probably the UI will be nicer:
data () {
return {
settings: {
bio_bg: {
url: 'http://via.placeholder.com/350x150',
}
},
bio: {}
}
}

Categories