website/tutorials/vuejs/01-wrapper.mdx
:::info
The following describes a relatively simple example that only allows for a single series to be rendered. This example can be used as a starting point and could be tweaked further using our extensive API.
Please note: this example is intended to be used with Vue.js 3
:::
This guide will focus on the key concepts required to get Lightweight Charts™ running within a Vue component. Please note this guide is not intended as a complete step-by-step tutorial. The example Vue components can be found at the bottom of this guide.
If you are new to Vue.js then please have a look at the official Vue.js tutorials before proceeding further with this example.
The example Vue wrapper component has the following features.
The ability to:
The example may not fit your requirements completely. Creating a general-purpose declarative wrapper for Lightweight Charts™ imperative API is a challenge, but hopefully, you can adapt this example to your use case.
Presented below is the finished wrapper component which is discussed throughout this guide. The interactive buttons beneath the chart are showcasing how to interact with the component and that code is provided below as well (within the example app component).
import BrowserOnly from '@docusaurus/BrowserOnly';
<BrowserOnly fallback={<div>Loading...</div>}> {() => { require('./assets/web-component.js'); return <vue-example></vue-example>; }} </BrowserOnly>
Vue components can be authored in two different API styles: Options API and Composition API.
This example will make use of the Composition API, but complete code examples for both APIs will be presented at the end of the tutorial.
The Vue component could be used within any Vue setup, you can learn more on the Vue documentation site: Ways of Vue
Refs for storing API instancesThe preferred way to store a reference to the created chart
(IChartApi instance), or any other of the
library's instances, is to make use of a plain JS variable or class field
instead of using Vue's ref
functionality.
When Vue wraps an object in a reference object, it modifies the object (to
enable reactivity) in such a way that it interferes with the internal logic of
the Lightweight Charts™. This can lead to unexpected behaviour. If you really need
to use a ref then please
consider using
shallowRef
instead.
We can instead create a variable to hold these instances outside of any vue
hooks (such as
onMounted,
watch) within the body of
the script.
<script setup>
import { onMounted } from 'vue';
// variable to store the created chart instance
let chart;
onMounted() {
// ...
}
</script>
onMounted lifecycle hook to create the chartLightweight Charts™ requires an html element to use as its container, you can
create a simple div element within the component's template and ask Vue to
create a reference to that element by adding the ref="chartContainer"
attribute to the div element and the corresponding variable within the script
section: const chartContainer = ref();
The ideal time to create the chart is during the mounted lifecycle hook
provided by the Vue component. The container div will be created and ready for
use at this stage. Within the
onMounted
hook we can call Lightweight Charts™ createChart
constructor and pass it the value of the container reference (which is the div
element).
:::tip
Remember to also clean up when the component is unmounted
(onUnmounted
hook) by calling the remove method on
the saved chart instance.
:::
<script setup>
import { onMounted, ref } from 'vue';
import { createChart } from 'lightweight-charts';
let chart;
const chartContainer = ref();
onMounted(() => {
// Create the Lightweight Charts Instance using the container ref.
chart = createChart(chartContainer.value);
});
onUnmounted(() => {
if (chart) {
chart.remove();
chart = null;
}
});
</script>
<template>
<div class="lw-chart" ref="chartContainer"></div>
</template>
<style scoped>
.lw-chart {
height: 100%;
}
</style>
A simple way to provide customisation of the chart to the component's consumers
is to create component properties for the options you wish to be customised.
Lightweight Charts™ has a variety of customisation options which can be applied
through the applyOptions method
on an Api instance (such as IChartApi,
ISeriesApi,
IPriceScaleApi, and
ITimeScaleApi).
We can define properties for use as the components API as follows:
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
type: {
type: String,
default: 'line',
},
data: {
type: Array,
required: true,
},
chartOptions: {
type: Object,
},
seriesOptions: {
type: Object,
},
});
</script>
These properties can be used during the creation of Api instances, for example:
chart = createChart(chartContainer.value, props.chartOptions);
We can instruct Vue to
watch these properties for
changes and allow us to provide code to react to these changes. Using this
mechanism, we can provide a direct mapping between the options properties and
the applyOptions methods on the instance. This allows the consumer of the
component to apply changes to the current options at any point during the
lifecycle of the chart.
:::info
Please note: the current options aren't reset when applying the new options, and the new options can be a partial object. Thus it is possible to change one option at a time while still keeping the current options.
:::
watch(
() => props.chartOptions,
newOptions => {
if (!chart) {
return;
}
chart.applyOptions(newOptions);
}
);
watch(
() => props.priceScaleOptions,
newOptions => {
if (!chart) {
return;
}
chart.priceScale().applyOptions(newOptions);
}
);
There may be cases where you want to provide access to the chart instance, or
provide useful methods, to the consumer of the component. This can be achieved
with the
defineExpose hook
provided by Vue.
import { defineExpose } from 'vue';
// A simple method to call `fitContent` on the time scale
const fitContent = () => {
if (!chart) {
return;
}
chart.timeScale().fitContent();
};
// Expose the chart instance via a method
const getChart = () => chart;
defineExpose({ fitContent, getChart });
The consumer of the component can create a reference to a specific instance of the component and use the reference's value to evoke one of the exposed methods.
<script setup>
import { ref } from 'vue';
import LWChart from './components/LWChart.vue';
// ...
const myChart = ref();
const fitContent = () => {
// call a method on the component.
myChart.value.fitContent();
};
</script>
<template>
<LWChart type="line" :data="myData" ref="myChart" />
<button type="button" @click="fitContent">Fit Content</button>
</template>
Presented below is the complete component source code for the Vue components. We have also provided a sample Vue App component which showcases how to make use of these components within a typical Vue application.
You can view a complete Vue project using these components at this StackBlitz example.
The following code block contains the source code for the sample Vue component using the Composition API.
<p><a href={require('!!file-loader!./assets/composition-api.vue').default} download="lw-chart.vue" target="\_blank">Download file</a></p>import CodeBlock from '@theme/CodeBlock'; import InstantDetails from '@site/src/components/InstantDetails'; import compositionCode from '!!raw-loader!./assets/composition-api.vue';
<InstantDetails> <summary>Click here to reveal the code.</summary> <CodeBlock className="language-html">{compositionCode}</CodeBlock> </InstantDetails>The following code block contains the source code for the sample Vue component using the Options API.
<p><a href={require('!!file-loader!./assets/options-api.vue').default} download="lw-chart.vue" target="\_blank">Download file</a></p>import optionsCode from '!!raw-loader!./assets/options-api.vue';
<InstantDetails> <summary>Click here to reveal the code.</summary> <CodeBlock className="language-html">{optionsCode}</CodeBlock> </InstantDetails>The following code block contains the source code for a sample Vue Application component which makes use of the Vue components shown above. It showcases a few ways to control and interact with the component.
<p><a href={require('!!file-loader!./assets/app.vue').default} download="app.vue" target="\_blank">Download file</a></p>import appCode from '!!raw-loader!./assets/app.vue';
<InstantDetails> <summary>Click here to reveal the code.</summary> <CodeBlock className="language-html">{appCode}</CodeBlock> </InstantDetails>