yoyoma
yoyoma

Reputation: 3536

Vuejs modal component cannot emit after axios call

My app is declared like this in app.js

Vue.component('sale', require('./components/Sale.vue'));

const app = new Vue({
el: '#app',
data: {
    showModal: false
},
});

My main index.blade file @yields the content of my view, and this view simply contains my custom component like this

<sale v-if="showModal" @hide="showModal=false"></sale>

As you can see, its using the showModal to determine when it shows the modal form on top of the main page.

The component in brief is declared here with the form submit option:

<template name="sale">
  <form class="form-horizontal" role="form" action="/sales/save" method="POST" @submit.prevent="save">

This save method is called correctly and the axios post is working successfully.

I have a standard form inside the modal page and the methods are declared here, which u can see uses axios to post to my controller - again, the post is successful.

methods: {
  close(){        
    this.$emit('hide');
  },
  save(){
    console.log('SOLD');
    console.log(this.$data);
    axios.post('/sales/save', this.$data)
      .then(function (response) {
        this.$emit('hide');
      })
      .catch(function(error) {
        console.log('NO ' , error);
      });
  }
}

The close method also works as I expect, using the emit to change the value of showModal, closing the dialog.

What is not working, is after the post, the .then function is giving me an error with the same call to emit: TypeError: Cannot read property '$emit' of undefined.

In fact, the .catch is catching the error with the emit inside the .then function (not what i was expecting - that it would be catching axios POST errors).

Essentially i'm not sure how to close the modal in the same manner, once the axios call is successful.

Edit: added:

mounted() {
  const self = this;
}

and changed emit reference

self.$emit('hide');

I'm not sure i understand the rest of that post

Upvotes: 0

Views: 3891

Answers (3)

Egor Stambakio
Egor Stambakio

Reputation: 18136

You assume this refers to Vue instance, but it's not the case in your callback. You lose the this reference in a regular function.

You can preserve this using one of the following solutions:

1) Use arrow function:

methods: {
    fetchData: function() {
        axios.get('https://jsonplaceholder.typicode.com/posts/1')
        .then(text => console.log(this)) // Vue instance
    }
}

2) Save this to a variable:

methods: {
    fetchData: function() {
        const self = this; // save reference
        axios.get('https://jsonplaceholder.typicode.com/posts/1')
        .then(function(text) {
            console.log(self) // Vue instance; 'this' points to global object (Window)
        })
    }
}

Upvotes: 4

craig_h
craig_h

Reputation: 32704

Considering you are using ES2015, you can simply use an arrow function to make sure you don't create a new this context:

   axios.post('/sales/save', this.$data)
      .then(response => {
        this.$emit('hide');
      })
      .catch(error => {
        console.log('NO ' , error);
    });

You can find out more about arrow functions at: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Upvotes: 3

Daniel Beck
Daniel Beck

Reputation: 21485

The reason the code in your update didn't work is that your self is scoped inside mounted(), but you're trying to use it inside methods.save().

This would work:

methods: {
  save() {
    var self = this;
    axios.post('/sales/save', this.$data)
      .then(function (response) {
        self.$emit('hide');
      })
  //...

Or if you're using an ES6 transpiler you could use arrow functions to avoid the scoping problem altogether (arrow functions don't bind their own this.)

methods: {
  save() {
    axios.post('/sales/save', this.$data)
      .then((response) => {
        this.$emit('hide');
      })

Upvotes: 1

Related Questions