javascriptvue.jsvuejs3bootstrap-datepicker

What causes this failure to introduce a Bootstrap date picker in a Vue 3 app?


I am working on a Vue 3 and Bootstrap 5 app.

Needing a date picker that is compatible with Bootstrap, I choose bootstrap-datepicker. I installed it via yarn.

In main.js:

import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css'
import 'bootstrap/dist/js/bootstrap.js'
import 'bootstrap-datepicker/dist/js/bootstrap-datepicker.js'   
import { createApp } from 'vue'
import router from 'vue-router'
import VueAxios from 'vue-axios'
import axios from 'axios'
import App from './App.vue'

createApp(App)
              .use(router)
              .use(VueAxios, axios)
              .provide('$apiBaseUrl', 'http://apisource.com/api')
              .mount('#app')

In components\Ui\DatepickerFrom.vue:

<template>
        <div class="input-group datepicker" id="date_from">
            <input type="text" class="form-control" />
            <span class="input-group-append">
                <span class="input-group-text icon d-block">
                    <i class="fa fa-calendar"></i>
                </span>
            </span>
        </div>
</template>

<script>
export default {
  name: 'DatepickerFrom',

  mounted() {
    document.getElementById('date_from').datepicker();
  }
}
</script>

UPDATE

in public/index.html I added, imediatelly before </body>:

<script src="<%= BASE_URL %>/node_modules/jquery/dist/jquery.slim.min.js"></script>
<script src="<%= BASE_URL %>/node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js"></script>
<script>
  (function(){
    $('#date_from').datepicker();
  })();
</script>

The problem persists.

The problem

Allthough, aparentlly, all the resources are available, the line document.getElementById('date_from').datepicker() throws this error in the console:

Uncaught TypeError: document.getElementById(...).datepicker is not a function

Where is my mistake?


Solution

  • Problems

    bootstrap-datepicker does not augment document (or any other native API), so the following code would fail:

    // ❌ The `.datepicker` function does not exist
    document.getElementById('date_from').datepicker();
    

    In your second attempt, the <script> block that tries to load the datepicker most likely occurs before Vue has rendered the required template to the DOM, so the #date_from element could not be found.

    Solution

    bootstrap-datepicker is a jQuery extension, and it requires jQuery to be installed globally (on window) to run. Make sure to include the CDN link for jquery and bootstrap-datepicker before importing your Vue app in index.html, as seen in this example Vite index.html:

    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <link rel="icon" href="/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Vite App</title>
        <script
          src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
          integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
          crossorigin="anonymous"
          referrerpolicy="no-referrer"
        ></script>
        <script
          src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"
          integrity="sha512-T/tUfKSV1bihCnd+MxKD0Hm1uBBroVYBOYSk1knyvQ9VyZJpc/ALb4P0r6ubwVPSGB2GvjeoMAJJImBG12TiaQ=="
          crossorigin="anonymous"
          referrerpolicy="no-referrer"
        ></script>
        <link
          rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.standalone.min.css"
          integrity="sha512-TQQ3J4WkE/rwojNFo6OJdyu6G8Xe9z8rMrlF9y7xpFbQfW5g8aSWcygCQ4vqRiJqFsDsE1T6MoAOMJkFXlrI9A=="
          crossorigin="anonymous"
          referrerpolicy="no-referrer"
        />
      </head>
      <body>
        <div id="app"></div>
        <script type="module" src="/src/main.js"></script>
      </body>
    </html>
    

    The only way to use the library is through a jQuery call. Your component should use jQuery in the mounted() hook, and pass it a template ref to the container element instead of an element ID so that you could have multiple components on the same page if needed:

    // DatepickerFrom.vue
    <template>
      <div class="input-group datepicker" ref="datepicker">
        <input type="text" class="form-control" />
        <span class="input-group-append">
          <span class="input-group-text icon d-block">
            <i class="fa fa-calendar"></i>
          </span>
        </span>
      </div>
    </template>
    
    <script>
    export default {
      mounted() {
        const $ = window.jQuery
        $(this.$refs.datepicker).datepicker()
      }
    }
    </script>
    

    demo

    While it's possible to use this library in Vue, I recommend switching to a modern date-picker library that does not depend on jQuery. jQuery manipulates the DOM, which should be avoided as Vue manages its own virtual DOM, so a potential bug could appear when mixing the two.