1

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?

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

7 Comments

I assume everything you wrote goes into the component script, not the main instance, otherwise my bad. Anyway, I tried giving the prop a value of 5 : <likes :now="5"></likes> (I put :now cause he thought I was giving a string containing 5 not a number), but I still get the log The value of 'likes' property is undefined . I copied everything you gave me, no more no less, the only thing in the main Vue instance is the console.log that return that error (for now I didn't put the data() part cause I wanted to see if I could print the property value passed from the component).
You can see the code in action here codesandbox.io/s/j1n5w345y9 , as you can see in the console, the two properties return undefined
@K3nzie remove mounted () { console.log("The value of 'like' property is " + this.like) }, from the main Vue instance
But... that's the point of my question, I wanted to see those values in my main instance (also, because I need to use axios and it doesn't make me use it in the component), so you're telling me that all the code you wrote, is in the component scripts tags? I thought that you should put the logic in the main instance new Vue({..., I mean, I was treating the component like it should be just an "extension" of the main app (considering that if you dont put it inside the el: '#app' scope it doesn't even load).
@K3nzie Some of the approaches that you're talking about would actually be done different if we were dealing with an SPA. But you're dealing with an integrated approach here. If you want to stop the FOUC then you should consider using the v-cloak directive. The data management should happen in the component not on the main Vue instance. If you're concerned about revealing the id then use a slug or uuid and update your route model binding in Laravel to accept these and resolve DI.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.