|

How To Create A Child Component In VueJS

How To Create A Child Component In VueJS

We’re on a roll working with VueJS, components, and ajaxified buttons in our view files. It was helpful to start off with no JavaScript at all, then progressively add in JavaScript to make the user experience better. At this point our edit and delete buttons for a reply are fully dynamic. In this tutorial, we will create a child component in VueJS so that we can give the ajax treatment to the Favorite button.


Define A New Component

We want to convert the existing favorite button on a reply into a dedicated Vue component. So first, we’ll imagine how we want to reference the favorite component in the view file. How about <favorite></favorite>? This makes sense, and we’ll also want to pass in the $reply data from Laravel as a prop. So ultimately we will start with this markup in the blade file.

We’ll now create a new file named Favorite.vue within the components directory and add the <template> and <script> sections to begin.
create vue child component

Our goal in the reply.blade.php file is to be able to remove all of the commented out markup, and simply replace it with the new highlighted component.


Building The <template>

In the <template> area of the component is where the markup is placed. We need a button to represent the element a user can click to favorite or unfavorite a reply. We can add some markup like so then to the <template> area of the Vue component. We’ll start with this.

Notice we also set up the data() method which returns a favorite count defaulted to 1. Now we can run yarn run watch-poll to turn on automatic file watching and re compilation so that as we make changes we can visit the browser to see the result.


Defining A Child Component

The Favorite.vue component is to be a child of the Reply.vue component. This can be defined in Reply.vue as we see here. Two things need to happen. We need to import the component, and we also need to define it in the components property.

After a quick second, webpack should have done it’s recompile and we can check in the browser to see how things are going. It looks like the child component is now rendering quite nicely. Excellent!
child component rendered in the browser


Define an event listener on the child

Now we want to define an event listener on the Favorite child component. When someone clicks this button, the goal is to trigger a an es6 method that toggles which we will define shortly on the Vue instance. That click handler is set up like so.

That means we need to define the toggle() method now within the methods object of the Vue instance. In addition, the toggle() method is going to need access to information about the $reply in question. Well, we had passed this in as an attribute when we defined <favorite :reply=”{{ $reply }}”></favorite>. This means we can fetch that data so to speak in Vue via the props property. Highlighted here are the props and new method. Parent components communicate to child components via props.

This represents your fairly typical favorite type scenario. If the reply is already favorited, then unfavorite it, otherwise create a new favorite for the reply. In constructing the endpoint to send the axios request to, we are making use of the data we had accepted from the prop. Hopefully that makes sense.


Refactoring The Reply Model

We might need to update the backend in Laravel just a bit to get this to work correctly. It turns out we’ll need to use the $appends property on the Reply model. This property exists for when you are casting to an array or to json, allowing you to specify any custom attributes to append to that casting. We want the favorites count. So we can add this line to the Reply model.

How did we determine the string to use here? Well, the Reply model actually uses the Favoriteable trait. In that trait is a custom getter.

So we follow the convention and use ‘favoritesCount’ as the string to pass to $appends. To show that this now gives us access to the favorites count in the Vue side, we can load up a page with a reply that we know has a favorite. Now, inspect with Vue developer tools, and we see that very property that we are looking for.
laravel appends property example casting

Since we now have access to that count, we should remove the hard coding of a count in the Vue data object, and use this.reply.favoritesCount to set that value like we see.


Computed Attributes For :class

On clicking the button, the user should see state change on the button itself. It should look different based on whether that user has favorited the reply or not. How might we tackle that? What we are going to do is to remove the hard coding of a class on the button element inside the template. Instead, we will bind that element to a custom computed attribute named classes. Now classes() will actually be a method in the computed property of Vue. Based on a small amount of logic, the method will apply one style class, or a different style class. Here is the first stab at that.

Loading up a reply in the browser first displays the default class. Why? Because active is hard coded to false. Now, if we click the button, it does change state. How? Because in the toggle() method, if the else branch is taken we can see that this.active gets updated to true.
vue computed attribute custom class


Updating the count in real time

So we got the state change working pretty good, but we saw that the number on the favorite did not go up in real time. This is very easy to fix. In the toggle method when setting this.active to true, right after this just increment the count with this.count++;. Let’s see if that works.
increment count in ui


A Delete Favorite Endpoint Is Needed

In order to finish building out the Favorite child component in Vue, we need to add an endpoint on the Laravel side to allow for removing a favorite from a reply. We already have a FavoritesTest class, so we can just add a new test method to begin.

Really quickly, this test just says that if we’re signed in and we submit a favorite, then there should be 1 favorite in the database. If we then delete that favorite, there should be 0 favorites in the database. We know the test will fail for the usual reasons, so let’s just start building out the code we need to make it pass.

Of course we’ll need a route for this endpoint, and a new destroy() method on the FavoritesController.
web.php

FavoritesController.php

In the Favoriteable.php trait, we’ll now add the unfavorite() method.

With a quick run of the new test, we see everything works.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_an_authenticated_user_can_unfavorite_any_reply
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 1.3 seconds, Memory: 10.00MB

OK (1 test, 2 assertions)

One more thing we should do on the Laravel side is to set up a custom getter or custom attribute on the Favoriteable trait. We are doing this so that we can pass it into the Vue side. Add this method like so to Favoriteable.php

Now in the Reply.php model, add this to $appends like we discussed earlier.

Finally, if we inspect the Vue components in Vue developer tools, that attribute is now available and we can make use of it.
custom getter appends property


Back To Vue and Favorite.vue component

Now we make everything dynamic in the Favorite component like so.

It looks like it is working as we expect. Both the class and the count update in real time and accurately reflect the immediate status of the favorite. Pretty cool!
vue Favorite component example


How To Create A Child Component In VueJS Summary

With that, another fun tutorial is in the books. In this episode, we saw how to create a new Favorite component that is a child of the existing Reply component using VueJS. There were a few important points to remember such as making sure to import the child into the parent as well as defining the child inside the components property of the parent. We also had some good practice with passing data around using various bindings, props, and custom attributes along with the $appends property in Laravel.