Render a Vue app using a promise, and await user input

10,155

Here's a simple example which does the following:

  • The Vue component is instantiated from a template and appended to the <body> element, rather than from an existing DOM element (in case you don't want the UI to be initially visible).
  • The promise is only resolved with the inputted text when the submit button is clicked. The component instance is destroyed and removed from the DOM.

const InputUI = {
  template: '#input-ui',
  data() {
    return {
      value: '',
    };
  },
};

function getInput() {
  return new Promise(resolve => {
    const inputUI = new Vue(InputUI);
    
    inputUI.$once('submit', value => {
      inputUI.$destroy();
      inputUI.$el.remove();
      resolve(value);
    });
    
    inputUI.$mount();
    document.body.appendChild(inputUI.$el);
  });
}

getInput().then(value => alert(value));
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>

<template id="input-ui">
  <div>
    <input v-model="value">
    <button @click="$emit('submit', value)">Submit</button>
  </div>
</template>

If you're using single file components, you would structure your code similar to this:

InputUI.vue

<template>
  <div>
    <input v-model="value">
    <button @click="$emit('submit', value)">Submit</button>
  </div>
</template>

<script>

export default {
  data() {
    return {
      value: '',
    };
  },
};

</script>

main.js

import Vue from 'vue';
import InputUI from './InputUI.vue';

function getInput() {
  return new Promise(resolve => {
    const InputUIVue = Vue.extend(InputUI);
    const inputUI = new InputUIVue();

    inputUI.$once('submit', value => {
      inputUI.$destroy();
      inputUI.$el.remove();
      resolve(value);
    });

    inputUI.$mount();
    document.body.appendChild(inputUI.$el);
  });
}

getInput().then(value => alert(value));
Share:
10,155
dendog
Author by

dendog

Updated on June 30, 2022

Comments

  • dendog
    dendog almost 2 years

    I have a design question, I currently have a logic heavy JS script, which I have written as various promises and created a structure as the below:

    init()
        .then(result => doSomethingA(result)) 
        .then(result => doSomethingB(result))
        .then(result => loadVueApp(result))
    

    Now the loadVueApp() function calls does the following:

    new Vue({
      el : '#app',
      render : h => h(App)
    });
    

    Which renders my Vue app, and then the user can interact with the app, go to various screens, make selections which I store in a global EventBus type component.

    Now my question is, how should I pass the users choices back to my tower of promises? Should I be doing that at all?

    I could resolve the loadVueApp right away based on simply the app appearing and then later make a function call back to the logic heavy script - but this does not feel as clean.

    Any ideas?

    Thanks in advance.

  • dendog
    dendog about 6 years
    Thanks for this solution, it looks good - but this requires manual compilation or using the render function. I am using single file components.
  • Decade Moon
    Decade Moon about 6 years
    I'm not using render functions, there's nothing about my example that can't be translated to single file components.
  • dendog
    dendog about 6 years
    From Docs: However, if you are relying on mounting to an element with existing content as template (using the el option), you will still be subject to those limitations. I would like to avoid adding the compiler if possible or am I missing something?
  • Decade Moon
    Decade Moon about 6 years
    The only reason why my answer is written in that way is to make it runnable online. To make it a single file component, all you have to do is put the <template> and component definition code into a .vue file as normal (and remove template: '#input-ui'). Everything else stays the same.
  • dendog
    dendog about 6 years
    Hmm sorry @Decade Moon , not sure what I am doing wrong : - Component template requires a root element, rather than just text. But also if I remove the reference to the template, how will the call to new Vue() know where to get the HTML from?
  • Decade Moon
    Decade Moon about 6 years
    You might need to instantiate Vue.extend(InputUI) because of the way vue-loader builds the component.