基于prop动态扩展VueJs组件

时间:2018-08-13 11:55:57

标签: vuejs2 jsx vue-chartjs

首先,我想说这个问题不是一成不变的,因为我很愿意尝试其他建议,因为我可能已经完全错过了一种合适的方法。

我在JSX中使用Vue-ChartJs,但想达到一个点,在应用程序开始时,我不必扩展我需要的每个图表组件,例如:

    // Line bar component.
Vue.component('line-chart', {
    extends: VueChartJs.Line,

    props: ['data', 'options'],

    mounted() {
        this.renderChart(this.data, this.options)
    }
});

相反,我想基于一个可能会决定扩展内容的道具来动态扩展组件:

const getChart = (type) => {
    if (type === 'bar') {
        return VueChartJs.Bar;
    };
};

Vue.component('chart', {
    props: ['data', 'options', 'type'],
    extends: () => {
        getChart(this.type)
    },
    mounted() {
        this.renderChart(this.data, this.options)
    }
})

此方法当前不起作用,因为in扩展:它返回的错误t是undefined,未设置“ this”。

对于可能的解决方案,有什么建议吗? 预先感谢。

3 个答案:

答案 0 :(得分:1)

在您的示例中,type属性有效地决定使用哪个组件。内置属性is更适合此工作。请注意,这里我仍在注册所有组件,但是使用util函数会使代码非常简短。

<div id="app">
    <div>
        <!-- think of :is like the :type property in your example, it serves the same purpose -- deciding which component to use -->
        <component :is="chartTypeLine" :data="chartData" :options="chartOptions"></component>
        <component :is="chartTypeBar" :data="chartData" :options="chartOptions"></component>
    </div>

</div>

<script src="https://vuejs.org/js/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<script src="https://unpkg.com/vue-chartjs/dist/vue-chartjs.min.js"></script>
<script>

    const getChart = (type) => {
        switch (type) {
            case 'line-chart':
                return VueChartJs.Line;
            case 'bar-chart':
                return VueChartJs.Bar;
            default:
                throw new Error("wrong chart type");
        }
    };

    const chartTypes = ['line-chart', 'bar-chart'];

    //this makes registering all those components easier.
    chartTypes.forEach(type => {
        Vue.component(type, {
            extends: getChart(type),
            props: ['data', 'options'],
            mounted () {
                this.renderChart(this.data, this.options)
            }
        })
    });

    new Vue({
        el: '#app',
        data: {
            chartData: {
                labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
                datasets: [
                    {
                        backgroundColor: '#f87979',
                        data: [40, 39, 10, 40, 39, 80, 40]
                    }
                ]
            },
            chartOptions: {responsive: true, maintainAspectRatio: false},
            chartTypeLine: 'line-chart',
            chartTypeBar: 'bar-chart'
        }
    })
</script>

https://codepen.io/anon/pen/MBxRpP

答案 1 :(得分:0)

除了先前的答案,我今天早上还发现了另一种方法。第一次运行JSX文件时,它使用相同的foreach循环通过一个对象实例化每种图表类型,这会将它们全部注册在准备使用的范围内。

const chartNames = [
    {
        name: 'line-chart',
        type: VueChartJs.Line
    }, {
        name: 'bar-chart',
        type: VueChartJs.Bar
    },
    {
        name: 'horizontal-bar-chart',
        type: VueChartJs.HorizontalBar
    },
    {
        name: 'doughnut-chart',
        type: VueChartJs.Doughnut
    },
    {
        name: 'pie-chart',
        type: VueChartJs.Pie
    },
    {
        name: 'radar-chart',
        type: VueChartJs.Radar
    },
    {
        name: 'polar-area-chart',
        type: VueChartJs.PolarArea
    },
    {
        name: 'bubble-chart',
        type: VueChartJs.Bubble
    },
    {
        name: 'scatter-chart',
        type: VueChartJs.Scatter
    }];
const registerCharts = (chartNames) => {
    chartNames.forEach((chartName) => {
        Vue.component(chartName.name, {
            extends: chartName.type,
            mixins: [VueChartJs.mixins.reactiveProp],
            props: {
                options: Object
            },
            methods: {
                // Used in the alternate render version, this updates the chart in question.
                update() {
                    this.$data._chart.update()
                }
            },
            mounted() {
                this.renderChart(this.chartData, this.options)
            }
        });
    })
};
registerCharts(chartNames);

答案 2 :(得分:0)

我喜欢这个

图表.js

import VueChartJs from 'vue-chartjs'
import Vue from 'vue'

const getChart = (type) => {
  switch (type) {
    case 'LineChart':
      return VueChartJs.Line
    case 'BarChart':
      return VueChartJs.Bar
    case 'PieChart':
      return VueChartJs.Pie
    default:
      throw new Error('wrong chart type')
  }
}

// generate component via fun
// it mean that you will generate as many components as you need

export default (type) => {
  return Vue.component(type, {
    extends: getChart(type),
    name: type,
    props: ['data', 'options'],
    mounted () {
      this.renderChart(this.data, this.options)
    }
  })
}

index.vue

<template> 
..
        <PieChart
          :data="testData"
          :options="options"
        />
..
</template>

<script>
import Chart from '~/components/UI/Chart/index'
..
export default {
  components: {
    PieChart: Chart('PieChart')
  }

}
</script>