No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    [How to] load external dependencies (CDN) ASYNC

    Show & Tell
    2
    3
    3357
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • benoitranque
      benoitranque last edited by benoitranque

      The Problem

      So recently I came upon a problem: I needed to use Google Map API

      The Google Maps API cannot be simply included as a NPM dependency, and you need a key to use it

      Of course I could request it in src/index.html like so

      
      <!DOCTYPE html>
      <html>
        <head>
          <meta charset="utf-8">
          <meta name="format-detection" content="telephone=no">
          <meta name="msapplication-tap-highlight" content="no">
          <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
      
          <title>My awesome page</title>
          <link rel="icon" href="statics/img/favicon.ico" type="image/x-icon">
        </head>
        <body>
          <div id="q-app"></div>
          <!-- built files will be auto injected -->
      
          <script src="https://maps.googleapis.com/maps/api/js?key=xxxxxxx">
      
        </body>
      </html>
      
      

      This is not ideal

      Say you have a large web app. You will load the google maps api on first load. Even if you only use it in one page of your SPA. This means clients download a library they may not need at a moment they definitely dont need it.

      So add the deffer atribute

      
         <script deffer src="https://maps.googleapis.com/maps/api/js?key=xxxxxxx">
      
      

      This is better

      This means the loading of this library is done last. However clients still download it whether they need it or not.

      Still not ideal

      Ideally we want the api to load when needed, if it is not in memory yet. How do we do this? Using this script:

      // src/externalApi.js
      
      let api = {}
      export default {
        config: function configApi (id, url) {
          if (!api[id]) {
            api[id] = {
              config: {
                id,
                url
              }
            }
          }
          else {
            // atempted creating same api multiple times
            // error code goes here
            console.error(`Atempted to create API ${id} multiple times`)
          }
        },
        load: function loadApi (id) {
          if (api[id]) {
            if (!api[id].promise {
              if (!document.getElementById(api[id].config.id)) {
                api[id].promise = new Promise((resolve, reject) => {
                  let script = document.createElement('script')
                  script.src = api[id].config.url
                  script.async = true
                  script.id = api[id].config.id
                  script.onload = () => {
                    resolve()
                  }
                  script.onerror = (err) => {
                    reject(err)
                    // not sure about this part. ESLint wants error to be passed, not sure if doing it properly
                  }
                  document.body.appendChild(script)
                })
              }
              else {
                // atempted to create api but id already exists
                // error code
               console.error(`Make sure the id ${id} is unique and is not used anywhere else in your app.`)
              }
            })
            return api[id].promise
          }
          else {
            // api has not been configured
            // error
            console.error(`API ${id} does not exist. Have you configured it in main.js?`)
          }
        }
      }
      
      

      What this does

      This allows us to configure our api in main.js, but load it only once requested. If the api has already been loaded, it will not be loaded again.

      Usage:

      
      // main.js
      import externalApi from '~src/externalApi'
      
      externalApi.config('google-api', 'https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxxxxxxxxxxx')
      
      // some component or view
      import { externalApi } from 'quasar'
      
      ...
      externalApi.load('google-api')
      .then(() => {
        console.log('google api is loaded')
      })
      ...
      // allternative
      
      mounted: async function () {
        await externalApi.load()
        console.log('api now loaded')
      }
      
      

      Hope this helps!

      B 1 Reply Last reply Reply Quote 5
      • B
        blini @benoitranque last edited by benoitranque

        @benoitranque thanks, ur code is broken, I just fixed it

        
        let api = {}
        
        export default {
          config: function configApi(id, url) {
            if (!api[id]) {
              api[id] = {
                config: {
                  id,
                  url
                }
              }
            }
            else {
              // atempted creating same api multiple times
              // error code goes here
              console.error(`Atempted to create API ${id} multiple times`)
            }
          },
          load: function loadApi(id) {
            if (api[id]) {
              if (!api[id].promise) {
                if (!document.getElementById(api[id].config.id)) {
                  api[id].promise = new Promise((resolve, reject) => {
                    let script = document.createElement('script')
                    script.src = api[id].config.url
                    script.async = true
                    script.id = api[id].config.id
                    script.onload = () => {
                      resolve()
                    }
                    script.onerror = (err) => {
                      reject(err)
                      // not sure about this part. ESLint wants error to be passed, not sure if doing it properly
                    }
                    document.body.appendChild(script)
                  })
                }
                else {
                  // atempted to create api but id already exists
                  // error code
                  console.error(`Make sure the id ${id} is unique and is not used anywhere else in your app.`)
                }
              }
              return api[id].promise
            }
            else {
              // api has not been configured
              // error
              console.error(`API ${id} does not exist. Have you configured it in main.js?`)
            }
          }
        }
        
        1 Reply Last reply Reply Quote 0
        • benoitranque
          benoitranque last edited by

          Thanks, where was it broken exactly? This is an old post so I forgot a lot of this, here is a newer post with a solution for 0.15

          1 Reply Last reply Reply Quote 0
          • First post
            Last post