26

Is it possible to use method overriding in Vue.js?

var SomeClassA = Vue.extend({
  methods: {
    someFunction: function() {
      // ClassA some stuff
    }
  }
});

var SomeClassB = SomeClassA.extend({
  methods: {
    someFunction: function() {
      // CALL SomeClassA.someFunction
    }
  }
});

I want to call ClassA someFunction from ClassB someFunction. Is it even possible?

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
MyFantasy512
  • 690
  • 1
  • 9
  • 20

3 Answers3

21

No, vue doesn't work with a direct inheritance model. You can't A.extend an component, as far as I know. It's parent-child relationships work mainly through props and events.

There are however three solutions:

1. Passing props (parent-child)

var SomeComponentA = Vue.extend({
    methods: {
        someFunction: function () {
            // ClassA some stuff
        }
    }
});

var SomeComponentB = Vue.extend({
   props: [ 'someFunctionParent' ],
   methods: {
       someFunction: function () {
           // Do your stuff
           this.someFunctionParent();
       }
   }
});

and in the template of SomeComponentA:

<some-component-b someFunctionParent="someFunction"></some-component-b>

2. Mixins

If this is common functionality that you want to use in other places, using a mixin might be more idiomatic:

var mixin = {
    methods: {
        someFunction: function() {
            // ...
        }
    }
};

var SomeComponentA = Vue.extend({
    mixins: [ mixin ],
    methods: {
    }
});

var SomeComponentB = Vue.extend({
   methods: {
       someFunctionExtended: function () {
           // Do your stuff
           this.someFunction();
       }
   }
});

3. Calling parent props (parent-child, ugly)

// In someComponentB's 'someFunction':
this.$parent.$options.methods.someFunction(...);
nils
  • 25,734
  • 5
  • 70
  • 79
  • 1. It's pretty good solution when it comes to last resort, but it's not very extensible. 2. I've thought about using Mixins here, but necessity of changing function names is not extensible either. 3. Can u even get this working!? http://jsfiddle.net/9s146cdr/1/ . This would be best bet, if only it was true :) – MyFantasy512 Apr 21 '16 at 10:06
  • I really don't recommend using #3 and I only included it for reference. The other two are much more idiomatic for vue (inheritance generally doesn't make too much sense, since componentA is not an abstract class but a parent component in the DOM with different responsibilities than componentB). It probably helps if you think less about extensibility and more about component responsibilities. – nils Apr 21 '16 at 10:10
  • You can't use #3 because it's not working :) I cannot agree with _since componentA is not an abstract class but a parent component in the DOM with different responsibilities than componentB_ because I miss-named classes, it should be `SomeAbstractComponentA`. I come to Vue from Ember, and in my opinion inheritance in components isn't anything wrong. – MyFantasy512 Apr 21 '16 at 10:16
  • Also, your example of using vue is not correct. You shouldn't call `new` on a component. I'll prepare an example... This is not Ember ;) – nils Apr 21 '16 at 10:16
  • Of course this fiddle was just to prove that #3 option is not working. I am instancing components in templates. – MyFantasy512 Apr 21 '16 at 10:19
  • Stepping aside **component responsibilities**, you cannot say that **extensibility** using simple concept like overriding methods, is too much abstraction for any framework. – MyFantasy512 Apr 21 '16 at 10:22
  • Well, option 3 only works in parent-child relationships of components. As I said, you can't do `A.extend`. If you want to extend from an independent component (not in a parent-child relationship), mixins are the only options at the moment. Actual method overriding was only introduced in ES6 classes in JavaScript, it was not native to the language before. – nils Apr 21 '16 at 10:25
  • If you really need direct extensibility, you could always write a plugin – nils Apr 21 '16 at 10:31
  • Thanks for clarification, as I see Vue extend functionallity doesn't provide inheritance functionallity. In that case, Mixins are best solutions. Not what I expected, but thanks for giving the right direction in Vue, thanks – MyFantasy512 Apr 21 '16 at 10:41
  • So I will just post some quick super call implementation, maybe someone would find this useful one day :) https://jsfiddle.net/dtq6obLp/ – MyFantasy512 Apr 21 '16 at 11:38
  • Late to the party and newer Vue dev, but everything I've read thus far in blogs and official documentation discourages option 1 in favor of using `$emit` in the child and handling the event in the parent. – akousmata Feb 25 '20 at 16:19
14

In case someone's interested in a JustWorksTM solution:

var FooComponent = {
  template: '<button @click="fooMethod()" v-text="buttonLabel"></button>',

  data: function () {
   return {
     foo: 1,
     bar: 'lorem',
     buttonLabel: 'Click me',
   }
  },

  methods: {
    fooMethod: function () {
      alert('called from FooComponent');
    },
    
    barMethod: function () {
      alert('called from FooComponent');
    },
  }
}

var FooComponentSpecialised = {
  extends: FooComponent,

  data: function () {
   return {
     buttonLabel: 'Specialised click me',
     zar: 'ipsum',
   }
  },

  methods: {
    fooMethod: function () {
      FooComponent.methods.fooMethod.call(this);
    
      alert('called from FooComponentSpecialised');
    },
  }
}

jsfiddle: https://jsfiddle.net/7b3tx0aw/2/


More info:

  1. This solution is for devs that can't use TypeScript for some reason (which I think allows defining vue components as classes, which in turn allows full inheritance feature-set).
  2. Further elaboration about the solution (whys and hows): https://github.com/vuejs/vue/issues/2977
  3. This ain't that ugly, considering that no rocket science is used here (calling anonymous functions with the this pointer replaced should be no magic for any decent js dev).

How to use Function.prototype.call()

Reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

Sample code:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"
caot
  • 3,066
  • 35
  • 37
d-ph
  • 572
  • 4
  • 17
  • I don't know if something has changed since this post but I needed `FooComponent.options.methods.fooMethod.call(this);` – Hamish Sep 09 '20 at 10:15
3

In case someone asks for a solution here is mine and works fine :

var SomeClassA = {
    methods: {
        someFunction: function () {
            this.defaultSomeFunction();
        },
        // defaultSomeFunction acts like parent.someFunction() so call it in inheritance
        defaultSomeFunction: function () {
            // ClassA some stuff
        },
    },
};

var SomeClassB = {
    extends: SomeClassA,
    methods: {
        someFunction: function () {
            // Replace the wanted SomeClassA::someFunction()
            this.defaultSomeFunction();
            // Add custom code here
        },
    },
};

using juste extends from https://v2.vuejs.org/v2/api/#extends replaces the usage of Vue.extends()

tony19
  • 125,647
  • 18
  • 229
  • 307
ybenhssaien
  • 3,427
  • 1
  • 10
  • 12