4

I have a vue2 component, where some methods are pretty long with a lot of callbacks and I would like to structurize it better. I try to follow the guidelines as per callbackhell.com but things don't look that easy within vue method.

Below is the sample of where I am at the moment, it works, but I am confused about the following:

  1. Function hoisting doesn't seem to work here, I need to define my functions first before I run them otherwise, it triggers an error. Why is that, am I missing something here?

  2. When I define inner functions like this, I lose the "this" bound to Vue component in them. I fix it by the .bind(this) after the function definition, it works but looks rather obscure. Is there any better way to define a utility function within methods while still keeping the context of "this"?

  3. Is this generally adding functions into method a good approach in Vue? I know I could make them sibling methods directly in methods: {} object and that resolves most of my issues, but as they are only relevant to the saveFavourites() method and not to anything else in the component, wrapping them within looks cleaner to me?

Thanks so much

methods: {
    saveFavourites() {
        var promptName = function() {
            return this.$swal({
                text: 'Enter name'),
                content: 'input',
                button: {
                    text: 'Save'),
                    closeModal: true,
                },
            })
        }.bind(this);

        var storePlan = function(name) {
            if(!name || (name = name.trim()) === '' ) return;
            axios.post('/api/user-store-daily-favourites', { meals: this.mealIds, name: name })
                .then(response => {
                    if(response.data.status === 'success') {
                        this.$emit('dailyFavouritesUpdated');
                    }
                });
        }.bind(this);

        // running it - if I move this above functions definitions, I get error
        promptName()
            .then( (name) => storePlan(name) );
    },
Ivan Kaloyanov
  • 1,748
  • 6
  • 18
  • 24
Jan Zikmund
  • 646
  • 9
  • 19

1 Answers1

4

For question 1, hoisting only applies to function declarations, not function expressions. Compare:

// Expression
var promptName = function() {

to:

// Declaration
function promptName() {

For further reading see:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function

For question 2, using bind is fine but you may find it easier to use arrow functions instead, which preserves the surrounding this value.

var promptName = () => {
  return this.$swal({
    ...
  })
}

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

For question 3, that seems like a matter of personal preference but generally I would move them to separate Vue methods. You have the usual trade offs to consider. Perhaps you feel the indirection is too high a price to pay and you'd rather keep all the code in one place. That's fine.

In this specific case I suggest you look into async/await instead of using all those extra functions. However, a word of caution, make sure you understand promises well before trying to use async/await as most problems with async/await come from not understanding the underlying mechanism used to implement it.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

The async/await version would look something like this:

async saveFavourites() {
  const name = await this.$swal({
    text: 'Enter name',
    content: 'input',
    button: {
      text: 'Save',
      closeModal: true,
    },
  });

  if(!name || (name = name.trim()) === '' ) return;

  const response = await axios.post('/api/user-store-daily-favourites', {
    meals: this.mealIds,
    name: name
  });

  if(response.data.status === 'success') {
    this.$emit('dailyFavouritesUpdated');
  }
}
skirtle
  • 27,868
  • 4
  • 42
  • 57
  • Thanks a lot! Just wondering - if I use function declaration to allow hosting as per 1, can I still use the arrow syntax or use .bind? – Jan Zikmund Jan 04 '20 at 19:46
  • @Ziki If you use a function declaration then it can't be an arrow function. Using `bind` would also be tricky as you couldn't just tag it on the end of the declaration: putting `bind` on the end would immediately make it an expression, not a declaration. Invoking the function using `call` would work to set the `this` value, though it'd be simpler to use the `const that = this` trick in the surrounding scope and use `that` inside the function. Just to be clear, I wasn't advocating the use of function declarations, merely explaining why hoisting doesn't apply with your original code. – skirtle Jan 04 '20 at 23:29