vue

Vue - Firing Emit On Parent Component - this.(functionName) not found

;TLDR

In components that execute methods based on events emitted from child components, make sure to set the vue instance = to a variable prior to any $emit.on functions within the parent component's mounted() section:

parent-emit-10.png

 

Background:

I have an addresses component, which is made up of several address components in vue.

Each address component consists of an <address> tag, an "Edit" button, an "Edit Form" and a "Delete" button (also another Form, specifically for deletions). After performing an action on the individual "address" components, I need to refresh the entire "addresses" component to remove any that have been deleted OR edited.  Also, you might notice the "Create Address" button. This is another component that consists of the button, and a "create address" form.

To Recap, the addresses component contains:

  1. A Create Address child component (button  and form)
  2. An address child component (the displayed data, an edit form and a delete form)

I am employing the use of the global event bus, setup in Laravel automatically.  Please keep this in mind.

parent-emit-1.png
parent-emit-2.png
parent-emit-3.png

Background 2  - setting Up the Emit:

In the "Address.vue" component

parent-emit-4.png
parent-emit-6.png

In the my "Delete.vue" component (the actual delete form):

parent-emit-5.png

In  the "Addresses.vue" component (where we are trying to catch the events):

parent-emit-7.png

 

 

Problem:

After catching an event from the global command bus, I received the following error: "Error in event handler for "address:deleted": "TypeError: this.reloadAddresses is not a function".

This happens because "this" references the event that is firing, vs the actual vue instance I am on.  As the "reloadAddresses" function exists outside that reference, it cannot be found. 

In NON-COMPONENT vue systems, I normally set the page's vue instance to "pageViewInstance", and I can then reference that throughout the page's life:

parent-emit-8.png

You would then replace "this.reloadAddresses()" with "pageVueInstance.reloadAddresses()"...or create a value within the mounted function " let vm = pageVueInstance;"

However, I don't think I can do that here, because I am actually performing this within another component (where we are "Exporting Default" at the top of the page) - I also don't want to clutter up the top of my template:

parent-emit-9.png

FIX:

Set the variable within the MOUNTED section of the Addresses component (the parent component), before the emit variables are called:

parent-emit-10.png

Setting the vm = this now referencees the Vue instance of the parent component.  As such, it now has access to the "reloadAddresses()" function created in the "methods" section.

Vue - Checkbox Verification Component

This post assumes you have already have Vue working within your application and have setup the ability to use components.  Laravel does this for you automatically, but setting that up is covered elsewhere (see here, herehere or here).

Background

Today I was working through a template I maintain for client projects.  It contains items that I continually have to setup for any project (roles, permissions, companies, locations, users).  I've been developing it since Laravel 5.3 (notably not long), but it has helped tremendously not only in saving time, but also implementing new pieces into an existing framework.

Case in point is that today, I was working through an aspect where I wanted to have the user check a checkbox verifying they wanted to delete a resource, prior to being able to click the delete button.  This is kind of a "are you suuuuuuure" without the implementation of a modal dialog box.

I've been turning to Vue a lot lately (as are all the cool kids), and this seemed like this request might check all my personal boxes for using Vue:

  1. Is this code likely to be reused throughout the application? Yep.  Anywhere I want the user to verify something prior to allowing them to proceed further.
  2. Does this take a non-trivial task, and make it super-easy? Yep.  Not that making a check-box trigger javascript is difficult...but it is something that can be done once and forgotten in Vue.
  3. Will it save time? I can make Vue do all kinds of cool things, but if I need to just change the color of a button, maybe just stick with plain old javascript.  Let's make sure this isn't one of those cases.  It's not!

My Requirements:

  1. I want it to be a reusable template, and allow for the use of slots.  This means that anything I put within the <ct-verify></ct-verify> tags would be hidden until the checkbox is checked.
  2. I want to pass through the verification message as a property.  This way it's not a generic "I agree" message.
  3. Setup a default message, in case I don't pass a verification message through.
  4. Create this as a component, to allow for the item to be used across various projects.


Here's What It'll Look Like

Before Click:

vue-verify-before-click.png

1. Notice that there is nothing in the HTML where the text should be.  Love me some Vue.


After Click:

vue-verify-after-click.png


1. Bam. Form available.

 

Additionally, when the checkbox is NOT clicked, the hidden html is actually removed from the page.  This means that it cannot be accessed by a dastardly individual with the means and attitude to cause a problem.  

When Clicked (see the form tag)

verify-clicked.png

 

When Not clicked

verify-not-clicked.png

 

Required coding within the blade/html/php page:
```
<ct-verify agreement="By clicking delete, I verify I am removing all data related to this company.">
    <b>WHATEVER YOU WANT</b><br>
    <button type="submit" class="button is-danger">ANYTHING</button>
</ct-verify>
```

Caveats


1. I use the Laravel framework as I love it.  But this tutorial can be done without the use of that as long as you pull in the appropriate items (https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js)
2. I am using the Bulma framework for the css styling.  I did this to avoid the use of jquery in the latest version of my template.

Solution

 

Component File

verify-component.png

Component Explanation:

  1. Props - two properties are required and both have defaults.  The first property is "agreement" and this is the text that you want the visitor to agree to prior to being "shown the goods".  The "selected" property is a boolean and is what we use to initially set the checkbox value.  There may be cases where you want it checked by default.  Passing through a value of "true" when the component is called will do this.
  2. Data - this contains one item "isSelected" which is a boolean set to false.  Per Vue 2.0 standards, we shouldn't edit a prop directly (they are supposed to be immutable), so we create a new value in the data section.
  3. mounted - after the component is loaded, we set the component's isSelected data value  = the selected property that was initially passed in.  This is the ONLY time we care what was passed in from the prop.  From here on out, it just uses the "isSelected" value to trigger visibility.
  4. methods - this is where the magic happens.  We have a function called updateSelected(), and we set the component's "isSelected" function = to the opposite of what it's currently set to (this.isSelected = ! this.isSelected).  WTH? 
  5. If you look at the <input> section of our template, you will see  the following code @change="updateSelected".  This is vue's way of saying "any time this checkbox is clicked, fire the updateSelected function."  The updateSelected function changes the component's isSelected value to the opposite of what it was before...effectively a toggle.

 

Calling From HTML / PHP
 

verify-html.png
  1. Notice that the only thing I am passing through is the agreement text.  As we are setting the "selected" value as a default of false, I have no need to pass that through as a property.
  2. The <ct-delete> form is another vue component I created. However you can place whatever you'd like instead of this.  I mean anything.

Files

Component-Verify.vue File

Component-Verify.html