<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: mstrVLT</title>
    <description>The latest articles on Forem by mstrVLT (@vlt).</description>
    <link>https://forem.com/vlt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3647878%2F48bc7801-2770-4051-a928-1c3d4b8340ab.png</url>
      <title>Forem: mstrVLT</title>
      <link>https://forem.com/vlt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vlt"/>
    <language>en</language>
    <item>
      <title>Vue + any js library (like D3.js, Chart.js etc)</title>
      <dc:creator>mstrVLT</dc:creator>
      <pubDate>Sun, 21 Dec 2025 11:26:23 +0000</pubDate>
      <link>https://forem.com/vlt/vue-any-js-library-like-d3js-chartjs-etc-4m57</link>
      <guid>https://forem.com/vlt/vue-any-js-library-like-d3js-chartjs-etc-4m57</guid>
      <description>&lt;p&gt;While the documentation suggests using &lt;strong&gt;ref&lt;/strong&gt;, &lt;strong&gt;useTemplateRef&lt;/strong&gt;, and &lt;strong&gt;onMounted&lt;/strong&gt;, this approach becomes cumbersome when dealing with multiple libraries. Instead of creating multiple wrapper components or using composables, we can use a custom directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const scopeMap = new WeakMap()
// enables v-el in templates
const vEl = {
  mounted: (el, binding) =&amp;gt; {
    const scope = effectScope();
    scopeMap.set(el, scope)
    scope.run(() =&amp;gt; binding.value &amp;amp;&amp;amp; (typeof binding.value === 'function') &amp;amp;&amp;amp; binding.value(el));
  },
  unmounted: (el) =&amp;gt; {
    const scope = scopeMap.get(el)
    scope &amp;amp;&amp;amp; scope.stop();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach simplifies the process significantly. You only need to add the &lt;strong&gt;v-el&lt;/strong&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v-el="barChart"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And write the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const barChart = (el) =&amp;gt; {
  console.log('mount')
  const chart = new Chart(
    el,
    {
      type: 'bar',
      data: {
        labels: data.value.map(row =&amp;gt; row.year),
        datasets: [
          {
            label: '',
            data: data.value.map(row =&amp;gt; row.count)
          }
        ]
      }
    }
  );

  watch(data, (newData) =&amp;gt; {
    console.log('update')
    chart.data.labels = newData.map(row =&amp;gt; row.year)
    chart.data.datasets[0].data = newData.map(row =&amp;gt; row.count)
    chart.update('none')
  }, {deep: true})
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the ChartJS example here &lt;a href="https://play.vuejs.org/#eNp9VW1v4zYM/iucB1wczLXb5HoYvKS4XVdgG7bbsA7Yh3MxKDad+ipLhiy7yRX570dJfksvbVDUkh6SD0mR1JP3c1WFbYNe7K3qVBWVhhp1U10loigrqTQ8wSPT6X0ACvMAMM8x1beprBAOkCtZwozUZxPx63umdADXTONWqv1tyjgG8EchkKlu84Gpaym0kpyjstsbjiUKPdhMjZHwc02GExFF0O8TYc2HCrdFrVH5nxIBz7imVC8yJeJu/lNnvD1DnohUipqiN6H9ySpYg8BH+A/ZA+38eY+3N5ygJ8NaykZozGLwkQewKURWiO0c1lcOBphYJJ1J6nxDbSR6tpCSbq3YAyIbwFA1wvet1Y4hbBlvEN68AV/vK5T5M2C9XsMsb0SqCylmcyN4JEA8c8d/CMz/RkwDedn/wdet9XXqpOFw3tZaUrKc9UTQn00w7lhZcRxyWNTFhhubVFO+Vo2J2EEZ06w7tzcLVFB7us0YFucX5wG5RK7GcHHeOX+EXwz44iS+GPUvT+HLUf8k/nbEF6fwywFfnuR/N+r/2OF3FHkf+4Zq1RQ3xT+5CCPlcNsDXV1aQd8xUN10VO4DYOoihhkZnHUQ2MzGowgAZxvkdWwBVxlhSZWu5KNhpk9oHJ8PBpwJKlTS6e7G/SY2B7vEPlJPHXiZzaamqyr3o/Lpl3f9sjuzH9fA4AaUbywH4FN2fqHVUR3bkWGZXdAuiUbsZMjfaPWBfzq/s+uX9adBOANNRRroz4QUOOv7fuwvyTHkcuufYLNmDgE8ZYhVDKZPDnRmm2oVuWlNc5o2Gqm9iOXKWV5lRUuAoVhtGq2lgPcpL9KHdeK5UWJ5KtOqiXdF31Xk5F7XGvv2u25J2t3qyMIq6j1YpUy0rKYRW+RkZ1CzM5cO+ponQ6vIyZLeKhoisvHVes/NciOzvbvUnIb6Wc7Kgu9jSLxfkbeoi5TBR2zIfADDCc1UJuqzGlWR2+xb1br4Qh1ysah29iyVXFKPfr9cLmlPGab8dpxe4Lmn7Yyuml4gKeittD4kHVAn3tBYiTe8VHSYePdaV3UcRWkm6CxDXrQqFKgjUZVRL/r+bXgZXkQ/YF0mXj86iVfTTBV5sX3GmsqyKug5+6syE/6YnXEuH3+3Z6ZeuhY0XmH6cOL8c71zjv6tkFLUmtz1mGaKRr2Db24/4o7WA1jKrDEX+Qr4D1J1N8ZHJ/ahERm5PZGz3v5mk0jP07/1zU6jqPugjKPjM0XF0+D1K6GP7i7Dy0kW/29RGZuUQALCxTvv8BU+Lt2P" rel="noopener noreferrer"&gt;play.vuejs.org&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you want to use AirDatepicker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const datepicker = (el) =&amp;gt; {
  const dp = new AirDatepicker(el, {
    timepicker: true,
    selectedDates: [currentDate.value],
    onSelect: ({date, formattedDate, datepicker}) =&amp;gt; {
      currentDate.value = Array.isArray(date) ? date[0] : date
    }
  })
  watch(currentDate, newCurrentDate =&amp;gt; {
    dp.selectDate(newCurrentDate)
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the AirDatepicker example here &lt;a href="https://play.vuejs.org/#eNqdVVFv2zYQ/iuECsQyakttvfXBtb10WYBtwLphGbCHqg+yeIqZUKRAUk5cw/+9d6Qky25SFAVaR+Tdfffdd+RxH1W5UMmdjeaRqGptHNuzwkDu4H1dswMrja7YaNvAKFOtAxnCdpLmdZ0EY6YKraxjUqh7tmRcF00FyiUB7FoCreIRmUfjd5mij2RjoETn0ca52s7TtODEhYMUW5MocKmqqzQXZsoRoxbFPZjLWfI2eXW2mRTWjjpQA5IwrdtJsBsAR5aezwZyniBtUPxqIySPKYYIYQFd3TH+H2eKsaTSDbF+gQGjcTSJ2npRrYUtjKgds+CaetWLs2cPuSs2E4aVTRiUJRTuptA1nGl5VFOY3/o6WpfT4oh+ptKUbacgO5ktYf6V11ipggf2P+T3uIqRdbBvr0mEPRXhawA+ZzHICVsLxYW6HbPlKpgZGyBizIBzTLqQR5ctwWo9it/wErXGxDQqjj1qmyHZ5rIBdnHBYrerQZdnhuUSm1Q2qnBCq9GYHE8cMM845D9M6LdRw0Ke599zvfVchyQpR2BrnUaxAnqm8F+nW9EYg8eEWoJY2MSY5KVlPEaozu3YHfQ6odPau8actNdL15J2omp358yZBnyNyBMkqg+cguycfRzwCbJ8ah21uvGuqMae2ExYqU2VuzZ2MqB4GIqFDM8hkep7Y/JdIqz/G1PomP3iIT6++sTm/ivEo1b441X1Jz0ewE2o5Kuhgn1aTmeH+HopT908GEEO+pBzLGNH4n4lbausB/qqFo/F6Zx6O6dTELrHXrLXoeNPCcB99kUabjXeZ1w4qGqJTivGKGwh8zWOFpR5mUUqryCLVoRBnZxfqLWt3y1S74Ph6C5U3bhQPd1cDDq2JIvaU4A3Aw0OHl23Rch9ApYGrLVZLVL8ocV+Pyjg4PuxWDfOacUuC4nwGB3kQ4L4gc3bYbD3QIBF2tflq/RzEj/Xmu+CzqVWblrmlZC7Ocui30FuscgiZx+gQU4T1u/gIMiVnVowovTS+lArPsOcvX5TPwa5tdR4yF/MZjNcB5XbnDhRwxicVnhh76xWOFk9h6w12Cyad2coi3B20jqLvvlgXKJbivOIGjPlusIn4+fkzduUC+uG+wnYaro2+gH5IwpWNsiT4uYWzBSF5mCoY9+X9yzsJPeZ7fn8pw/Ad6R+8o18ifjPgtKT+aPATyF1oxRb6nDGqlLcnjW00FUtJJi/a5r4p43NpdQPf/q9wTDEmA0U90/s39nHQP4fA17VQZ0uN3jpg/n65oO/Wr2x0ryR7SF6xvgvWC0b4hjcfm0UR9pDJYntH/584nP1n71+dKBsVxQRPT5b/jBdfaP0I91Z8lOv4uELwfA+sw==" rel="noopener noreferrer"&gt;play.vuejs.org&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
