Is it possible to nest methods in Vue.js in order to group related methods?

Viewed 6755

I'd like to group some of my Vue.js methods together in a sort of "submethod" class, but I only seem to be able to have single level methods.

For example, if I wanted to have a set of methods dealing purely with button actions:

new Vue({

    el: '#app',

    data: { },

    methods: {

        buttonHandlers: {

            handler1: function() {
                dosomething;
            },

            handler2: function() {
                dosomething;
            }

        }

    }

});

I would expect to be able to then use something like:

<button v-on:click="buttonHandlers.handler1">Click Me</button>

but nothing happens.

I have tried forcing the function to run by adding brackets:

<button v-on:click="buttonHandlers.handler1()">Click Me</button>

but I receive this console error:

Uncaught TypeError: scope.buttonHandlers.handler1 is not a function

I've setup a small https://jsfiddle.net/ozx9oc4c/ to demonstrate what I mean.

If anyone knows of a way to logically group functions under parent methods in Vue.js, rather than pages and pages of single level methods with no real structure, I'd be grateful for your knowledge.

5 Answers

There is actually a very simple technique: define your nested methods in the created hook:

created() {
  this.on = {
    test: () => {console.log(this)}
  }
  this.on.test();
}

NOTE: Two things, A) in this case you must use arrow function(s) and B) if this feels "hacky" to you, perhaps because of the cluttering of the created lifecycle hook, you can always delegate to a method, let's say this.namespaceMethods(), e.g.:

created() {
  this.namespaceMethods();
  // call namespaced methods
  this.foo.bar();
  this.foobar.baz();
  // etc.
},
methods: {
  this.namespaceMethods() {
    this.foo = {
      bar: () => {console.log("foobar")}
    },
    this.foobar = {
      baz: () => {console.log("foobarbaz")}
    }
  },
  // etc
}

I had the same issue (the need of a namespace) while writing a Vue mixin. This answer doesn't directly address your case, but it could give a clue.

This is how I defined the mixin.

export default {
   created () {
    // How to call the "nested" method
    this.dummy('init')

    // Pass arguments
    this.dummy('greet', {'name': 'John'})
   },

   // Namespaced methods
   methods: {
     dummy (name, conf) {
       // you can access reactive data via `that` reference,
       // from inside your functions
       const that = this

       return {
         'init': function (conf) {
            console.log('dummy plugin init OK')
         },
         'greet': function (conf) {
            console.log('hello, ' + conf['name'])
         }
       }[name](conf)
     }
   }
 }

PS: for an official solution, Evan You said no.

I use this pattern:

Template:

<ExampleComponent
  :test="hello"
  @close="(arg) => example('close')(arg)"
  @play="(arg) => example('next')(arg)"
  @stop="(arg) => example('back')(arg)"/>

Script:

...
methods: {
  test () {
    this.info('open')('test');
  },
  info (arg) {
    console.table({omg: [arg, '!']});
  },
  example (arg) {
    let self = this;
    const methods = {
      open (arg) {self.info(arg);},
      close (arg) { return self.example('play')(arg)},
      play (arg) {console.log(self, this)},
      stop () {console.error('Lo1')},
    };
    if (!Object.keys(methods).includes(arg)) return () => false;
    return methods[arg];
  },
}
...

And second case:

Script:

const fabric = (arg, foo, context) => {
  const methods = foo(context);
  if (!Object.keys(methods).includes(arg)) return () => false;
  return methods[arg];
};

export default {
  ...
  methods: {
    test () {
      this.info('open')('test');
    },
    info (arg) {
      console.table({omg: [arg, '!']});
    },
    example (arg) {
      return fabric(arg, (cnx)=>({
        open (arg) {cnx.info(arg);},
        close (arg) { return cnx.example('play')(arg)},
        play (arg) {console.log(cnx, this)},
        stop () {console.error('Lo1')},
      }), this);
    },
  }
  ...
}

Also, I think this is not a good practice, but it works and makes my work easier.

Related