Overriding a component method
Asked Answered
P

2

9

I'm using VueCharts which is a Google Charts plugin for vue.

I'm trying to give the newer Material Design looks to the charts. I've tried passing "google.charts.Bar" to chart-type prop and it did work. However, many of the options are ignored because, as stated in the Google Charts docs, the options object needs to be converted by using google.charts.Bar.convertOptions(options), something the plugin doesn't do.

Looking at the source, the plugin installs a 'vue-chart' component. This component uses ChartWrapper to handle loading the chart libraries, like so:

methods: {
    buildWrapper (chartType, dataTable, options, containerId) {
      let wrapper = new google.visualization.ChartWrapper({
        chartType: chartType,
        dataTable: dataTable,
        options: options,
        containerId: containerId
      })

      return wrapper
    },

So all I need is to override this method to convert the options before passing them to the ChartWrapper.

But how? I haven't found a way to simply override a component method in vue docs. I could create a new component and pass the converted options down, but I need access to the google object, which is only loaded internally by the plugin.

I have also read I could use mixins, but it's not clear how. This doesn't work:

Vue.component('MyCustomChart', {
    mixins: ['vue-chart'],
    methods: {
        buildWrapper (chartType, dataTable, options, containerId) {
            let wrapper = new google.visualization.ChartWrapper({
                chartType: chartType,
                dataTable: dataTable,
                options: google.charts.Bar.convertOptions(options), // that's all I need
                containerId: containerId
            })

            return wrapper
        },
    }
})

[Vue warn]: Failed to mount component: template or render function not defined. (found in MyCustomChart)

Probabilism answered 30/3, 2017 at 11:31 Comment(0)
U
10

You can use Vue's extend method to customize plugin components.

In your case:

import VueCharts from 'vue-charts'  
Vue.use(VueCharts);

const Base = Vue.options.components["vue-chart"];
const CustomChart = Base.extend({
  methods: {
    buildWrapper (chartType, dataTable, options, containerId) {
      let wrapper = new google.visualization.ChartWrapper({
        chartType: chartType,
        dataTable: dataTable,
        options: google.charts.Bar.coverOptions(options),
        containerId: containerId
      })

      return wrapper
    },
  })
}

Vue.component('MyCustomChart', CustomChart);

(credit to Bert Evans for noting that you need to reference the base component from Vue.options.components before extending to customize)

Ultranationalism answered 30/3, 2017 at 14:52 Comment(8)
I'm not sure this will work. vue-charts is a plugin. vue-chart is a globally added component.Cortney
It worked for me. If vue-chart component provides a property to specify the options, then overriding the method isn't the way to go. But, addressing the question, this is how I would override methods from a component plugin.Ultranationalism
Is there a reason you would choose lodash over Vue.extend?Cortney
how would you do that with a component plugin?Ultranationalism
In this case, extend twice; const Base = Vue.extend(VueChart), then const CustomChart = Base.extend({...}). Anyway, I was just curious. thanks :)Cortney
I ended up forking the repo and placing checks in buildWrapper to conditionally use convertOptions based on chartType. But thanks for showing how to customize the component. I'll try that later and accept the answer if it works.Probabilism
I believe I led you astray. See my updated answer. Ideally you can edit and @RegularEverydayNormalGuy will accept yours.Cortney
I had actually been testing with the wrong npm module 'vue-chart', which is just a component and not a plugin, which was why your initial comment worked for me. But I updated my answer so that it should work for the asker. Thanks for your help!Ultranationalism
C
6

I played around with this a bit and I misled @thanksd above with my recommendation on what to extend. One way that works is this:

import VueChart from "vue-charts";

Vue.use(VueChart);

const BaseChart = Vue.options.components["vue-chart"];
const CustomChart = BaseChart.extend({
  methods:{
    buildWrapper (chartType, dataTable, options, containerId) {
      let wrapper = new google.visualization.ChartWrapper({
        chartType: chartType,
        dataTable: dataTable,
        options: google.charts.Bar.convertOptions(options),
        containerId: containerId
      })

      return wrapper
    }
  }
});

Vue.component("custom-chart", CustomChart);

Further Explanation

As I thought, extending VueChart either through Vue's native extend, or through lodash, will not achieve the expected results. The result of importing VueChart is a plugin definition. Both lodash and Vue will happily accept that as an object to extend, but neither will result in a Vue component. Trying to use the result of either will result in the error mentioned in the question, "template or render function not defined". That error is absolutely true; extending VueChart extends an install function with a single method.

So how do you get the object to extend? vue-charts doesn't expose it. The install just calls Vue.component('vue-chart', Chart).

Fortunately, Vue makes the globally installed components through Vue.options.components. By extending Vue.options.components["vue-chart"] we get a proper Vue component definition.

Finally, I was surprised google was not available to @RegularEverydayNormalGuy. It has to be available; vue-chart uses it. But he's right, it's not available immediately. vue-chart loads the script asynchronously. Again, the plugin unfortunately doesn't make it available to you in any way, it just initializes itself after it's asynchronously loaded. There are ways to work around that, but at that point you should just probably submit a pull request.

Original Answer

options is a property on vue-chart. Why not just pass in the converted options?

new Vue({
    data:{
        convertedOptions: google.charts.Bar.convertOptions({<my options>})
    }
})

And in the template

<vue-chart :options="convertedOptions"></vue-chart>
Cortney answered 30/3, 2017 at 16:21 Comment(2)
@RegularEverydayNormalGuy You're right. It's not immediately available.Cortney
Thanks for the detailed answer. Wish I could accept both.Probabilism

© 2022 - 2024 — McMap. All rights reserved.