Is it possible to nest methods in Vue.js in order

2020-06-17 06:25发布

问题:

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.

回答1:

The closest I've got to doing this, is to declare the parent as a function, and return an object with a set of methods.

Example:

new Vue({

  el: '#app',

  data: {},

  methods: {

    buttonHandlers: function() {
      var self = this; // so you can access the Vue instance below.

      return {

        handler1: function() {
          dosomething;
          self.doSomething();
        },

        handler2: function() {
          dosomething;
        },

      },

    }

  }

});

And you can call the methods like this:

<button @click="buttonHandlers().handler1()">Click Me</button>


回答2:

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

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


回答3:

if i had that problem, i would use a click handler() to delegate request to other methods. eg:

new Vue({

    el: '#app',

    data: { },

    methods: {

        handler1: function() {
             console.log("handler 1 called");
        },

        handler2: function() {
            console.log("handler 2 called");
        },

        buttonHandler:function(callback){
            callback();
        }


    }

});

and use html as

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

<button v-on:click="buttonHandler(handler2)">Click Me</button>

The code is only for demo. In real life i will be passing a number or string argument in template and using switch case to determine handler.



回答4:

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.



回答5:

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.