title: Vue: Access the dom/template from a module?
body: <p>I have a Vue PWA with a ServiceWorker. When the ServiceWorker (registerServiceWorker.js) is imported it checks if there is a new version available, and if so I want to present the user the option to upgrade. But at that point I'm in a JavaScript module, how can I add a popup/message+button on the HTML document at that point?</p>
  
  <p>Ideally I would set-up my message beforehand using a Vuetify snackbar, or something similar on the App.vue or in the Navbar.vue which I could show/hide based on a global variable or event or something. </p>
  
  <p>However, there is a complicating factor: The button needs to execute js-code based on a parameter...</p>
  
  <p>main.js (simplified):</p>
  
  <pre><code>import Vue from 'vue'
  import App from './App.vue'
  import router from './router'
  import vuetify from './plugins/vuetify'
  import './registerServiceWorker'
  
  new Vue({
    router,
    vuetify,
    render: h =&gt; h(App)
  }).$mount('#app')
  </code></pre>
  
  <p>App.vue (simplified):</p>
  
  <pre><code>&lt;template&gt;
    &lt;v-app&gt;
      &lt;Navbar/&gt;
      &lt;v-content&gt;
        &lt;router-view&gt;&lt;/router-view&gt;
      &lt;/v-content&gt;
      &lt;Footer/&gt;
    &lt;/v-app&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  import styles from './app.css'
  import Navbar from '@/components/Navbar'
  import Footer from '@/components/Footer'
  
  export default {
    name: 'App',
    components: { Navbar, Footer },
  }
  &lt;/script&gt;
  </code></pre>
  
  <p>registerServiceWorker.js</p>
  
  <pre><code>function onNewServiceWorker(registration, callback) {
    if (registration.waiting) {
      // SW is waiting to activate. Can occur if multiple clients open and
      // one of the clients is refreshed.
      return callback();
    }
  
    function listenInstalledStateChange() {
      registration.installing.addEventListener('statechange', function(event) {
        if (event.target.state === 'installed') {
          // A new service worker is available, inform the user
          callback();
        }
      });
    };
  
    if (registration.installing) {
      return listenInstalledStateChange();
    }
  
    // We are currently controlled so a new SW may be found...
    // Add a listener in case a new SW is found,
    registration.addEventListener('updatefound', listenInstalledStateChange);
  }
  
  if (process.env.NODE_ENV === 'production') {
  
    window.addEventListener('load', function() {
      var refreshing
      // When the user asks to refresh the UI, we'll need to reload the window
      navigator.serviceWorker.addEventListener('controllerchange', function(event) {
        if (refreshing) return // prevent infinite refresh loop when you use "Update on Reload"
        refreshing = true
        console.log('Controller loaded')
        window.location.reload()
      })
  
      navigator.serviceWorker.register(`${process.env.BASE_URL}service-worker.js`,{})
      .then(function (registration) {
          // Track updates to the Service Worker.
        if (!navigator.serviceWorker.controller) {
          // The window client isn't currently controlled so it's a new service
          // worker that will activate immediately
          return
        }
        registration.update()
        onNewServiceWorker(registration, function() {
          //showRefreshUI(registration);
          console.log('TODO: show update message')
        })
      });
    });
  }
  </code></pre>
  
  <p>The commented out <code>showRefreshUI(registration)</code> call is what I need to have in my Vue template, and here is the complicating factor, it's based on the <code>registration</code> parameter:</p>
  
  <pre><code>function showRefreshUI(registration) {
    var button = document.createElement('button');
    button.style.position = 'absolute';
    button.style.bottom = '24px';
    button.style.left = '24px';
    button.textContent = 'This site has updated. Please click here to see changes.';
  
    button.addEventListener('click', function() {
      if (!registration.waiting) {
        // Just to ensure registration.waiting is available before
        // calling postMessage()
        return;
      }
  
      button.disabled = true;
  
      registration.waiting.postMessage('skipWaiting');
    });
  
    document.body.appendChild(button);
  };
  </code></pre>
  
  <p>FYI, the ServiceWorker registration is based on:</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/40100922/activate-updated-service-worker-on-refresh">Activate updated service worker on refresh</a>,</li>
  <li><a href="https://github.com/dfabulich/service-worker-refresh-sample/blob/refresh-last-tab/index.html" rel="nofollow noreferrer">https://github.com/dfabulich/service-worker-refresh-sample/blob/refresh-last-tab/index.html</a> and</li>
  <li><a href="https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68" rel="nofollow noreferrer">https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68</a></li>
  </ul>
  
  <p>I'm just starting to work with Vue and PWA. Please bear with me here ;-)</p>