Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 3 Support #476

Closed
jd-solanki opened this issue Sep 17, 2020 · 7 comments
Closed

Vue 3 Support #476

jd-solanki opened this issue Sep 17, 2020 · 7 comments

Comments

@jd-solanki
Copy link

As Vue 3 has RC It will be a good idea to add support for Vue 3.

Currently, if we use this package with Vue 3 we are getting error.

@mkschulze
Copy link

Agree, that would help getting ready.

@Daemach
Copy link

Daemach commented Oct 19, 2020

Vue 3 has been released. It is now the current version. When will this component be updated? We are building on Vue 3.

@ambit-tsai
Copy link

As Vue 3 has RC It will be a good idea to add support for Vue 3.

Currently, if we use this package with Vue 3 we are getting error.

Agree, that would help getting ready.

Vue 3 has been released. It is now the current version. When will this component be updated? We are building on Vue 3.

I've made one echarts-for-vue, it is compatible with Vue 3 and 2.

@terminalqo
Copy link

terminalqo commented Nov 3, 2020

What I found: https://www.npmjs.com/package/vue3-echarts 🤣 🤣

@lchrennew
Copy link

As Vue 3 has RC It will be a good idea to add support for Vue 3.

Currently, if we use this package with Vue 3 we are getting error.

Hi everybody,
I modified the ECharts.vue as follow, it works fine.

<template>
  <div class="echarts"/>
</template>

<style>
.echarts {
  width: 600px;
  height: 400px;
}
</style>

<script>
import echarts from 'echarts/lib/echarts'
import { debounce } from 'lodash-es'
import { addListener, removeListener } from 'resize-detector'

const INIT_TRIGGERS = ['theme', 'initOptions', 'autoresize']
const REWATCH_TRIGGERS = ['manualUpdate', 'watchShallow']
export default {
  props: {
    options: Object,
    theme: [String, Object],
    initOptions: Object,
    group: String,
    autoresize: Boolean,
    watchShallow: Boolean,
    manualUpdate: Boolean
  },
  data() {
    return {
      lastArea: 0
    }
  },
  watch: {
    group(group) {
      this.chart.group = group
    }
  },
  methods: {
    // provide an explicit merge option method
    mergeOptions(options, notMerge, lazyUpdate) {
      if (this.manualUpdate) {
        this.manualOptions = options
      }
      if (!this.chart) {
        this.init(options)
      } else {
        this.delegateMethod('setOption', options, notMerge, lazyUpdate)
      }
    },
    // just delegates ECharts methods to Vue component
    // use explicit params to reduce transpiled size for now
    appendData(params) {
      this.delegateMethod('appendData', params)
    },
    resize(options) {
      this.delegateMethod('resize', options)
    },
    dispatchAction(payload) {
      this.delegateMethod('dispatchAction', payload)
    },
    convertToPixel(finder, value) {
      return this.delegateMethod('convertToPixel', finder, value)
    },
    convertFromPixel(finder, value) {
      return this.delegateMethod('convertFromPixel', finder, value)
    },
    containPixel(finder, value) {
      return this.delegateMethod('containPixel', finder, value)
    },
    showLoading(type, options) {
      this.delegateMethod('showLoading', type, options)
    },
    hideLoading() {
      this.delegateMethod('hideLoading')
    },
    getDataURL(options) {
      return this.delegateMethod('getDataURL', options)
    },
    getConnectedDataURL(options) {
      return this.delegateMethod('getConnectedDataURL', options)
    },
    clear() {
      this.delegateMethod('clear')
    },
    dispose() {
      this.delegateMethod('dispose')
    },
    delegateMethod(name, ...args) {
      if (!this.chart) {
        this.init()
      }
      return this.chart[name](...args)
    },
    delegateGet(methodName) {
      if (!this.chart) {
        this.init()
      }
      return this.chart[methodName]()
    },
    getArea() {
      return this.$el.offsetWidth * this.$el.offsetHeight
    },
    init(options) {
      if (this.chart) {
        return
      }
      const chart = echarts.init(this.$el, this.theme, this.initOptions)
      if (this.group) {
        chart.group = this.group
      }
      chart.setOption(options || this.manualOptions || this.options || {}, true)
      Object.keys(this.$attrs).forEach(event => {
        const handler = this.$attrs[event]
        if (handler instanceof Function) {
          if (event.indexOf('zr:') === 0) {
            chart.getZr().on(event.slice(3), handler)
          } else {
            chart.on(event, handler)
          }
        }
      })
      if (this.autoresize) {
        this.lastArea = this.getArea()
        this.__resizeHandler = debounce(
            () => {
              if (this.lastArea === 0) {
                // emulate initial render for initially hidden charts
                this.mergeOptions({}, true)
                this.resize()
                this.mergeOptions(this.options || this.manualOptions || {}, true)
              } else {
                this.resize()
              }
              this.lastArea = this.getArea()
            },
            100,
            { leading: true }
        )
        addListener(this.$el, this.__resizeHandler)
      }
      Object.defineProperties(this, {
        // Only recalculated when accessed from JavaScript.
        // Won't update DOM on value change because getters
        // don't depend on reactive values
        width: {
          configurable: true,
          get: () => {
            return this.delegateGet('getWidth')
          }
        },
        height: {
          configurable: true,
          get: () => {
            return this.delegateGet('getHeight')
          }
        },
        isDisposed: {
          configurable: true,
          get: () => {
            return !!this.delegateGet('isDisposed')
          }
        },
        computedOptions: {
          configurable: true,
          get: () => {
            return this.delegateGet('getOption')
          }
        }
      })
      this.chart = chart
    },
    initOptionsWatcher() {
      if (this.__unwatchOptions) {
        this.__unwatchOptions()
        this.__unwatchOptions = null
      }
      if (!this.manualUpdate) {
        this.__unwatchOptions = this.$watch(
            'options',
            (val, oldVal) => {
              if (!this.chart && val) {
                this.init()
              } else {
                // mutating `options` will lead to merging
                // replacing it with new reference will lead to not merging
                // eg.
                // `this.options = Object.assign({}, this.options, { ... })`
                // will trigger `this.chart.setOption(val, true)
                // `this.options.title.text = 'Trends'`
                // will trigger `this.chart.setOption(val, false)`
                this.chart.setOption(val, val !== oldVal)
              }
            },
            { deep: !this.watchShallow }
        )
      }
    },
    destroy() {
      if (this.autoresize) {
        removeListener(this.$el, this.__resizeHandler)
      }
      this.dispose()
      this.chart = null
    },
    refresh() {
      if (this.chart) {
        this.destroy()
        this.init()
      }
    }
  },
  created() {
    this.initOptionsWatcher()
    INIT_TRIGGERS.forEach(prop => {
      this.$watch(
          prop,
          () => {
            this.refresh()
          },
          { deep: true }
      )
    })
    REWATCH_TRIGGERS.forEach(prop => {
      this.$watch(prop, () => {
        this.initOptionsWatcher()
        this.refresh()
      })
    })
  },
  mounted() {
    // auto init if `options` is already provided
    if (this.options) {
      this.init()
    }
  },
  activated() {
    if (this.autoresize) {
      this.chart && this.chart.resize()
    }
  },
  unmounted() {
    if (this.chart) {
      this.destroy()
    }
  },
  connect(group) {
    if (typeof group !== 'string') {
      group = group.map(chart => chart.chart)
    }
    echarts.connect(group)
  },
  disconnect(group) {
    echarts.disConnect(group)
  },
  getMap(mapName) {
    return echarts.getMap(mapName)
  },
  registerMap(mapName, geoJSON, specialAreas) {
    echarts.registerMap(mapName, geoJSON, specialAreas)
  },
  registerTheme(name, theme) {
    echarts.registerTheme(name, theme)
  },
  graphic: echarts.graphic
}
</script>

@fts1025
Copy link

fts1025 commented Jan 22, 2021

@lchrennew

Thank you, it works for me.
I wrote another version.

https://v3.vuejs.org/guide/migration/listeners-removed.html#_3-x-syntax

In Vue 3's virtual DOM, event listeners are now just attributes, prefixed with on, and as such are part of the $attrs object, so $listeners has been removed.

Object.keys(this.$attrs).forEach(attr => {
  const handler = this.$attrs[attr]

  if (attr.indexOf('zr:') === 0) {
    chart.getZr().on(attr.slice(3), handler)
  } else if (attr.indexOf('on') === 0) {
    chart.on(attr, handler)
  }
})

@Justineo
Copy link
Member

Hi everyone, sorry for the late response.

Based on Apache ECharts 5, vue-echarts@6 is published with support for both Vue 2 and 3. See our latest documentation here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants