Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I dynamically add questions to Inquirer JS during execution, using rxjs?

I want to ask questions to the user without having all the questions lined up immediately.

The documentation mentions rxjs but I feel there is a gap in the documentation on how to properly add questions while prompts are being executed, or at least it did not quite work for me.

https://www.npmjs.com/package/inquirer#reactive-interface

Internally, Inquirer uses the JS reactive extension to handle events and async flows.

This mean you can take advantage of this feature to provide more advanced flows. For example, you can dynamically add questions to be asked:

var prompts = new Rx.Subject();
inquirer.prompt(prompts);

// At some point in the future, push new questions
prompts.next({
  /* question... */
});
prompts.next({
  /* question... */
});

// When you're done
prompts.complete();

And using the return value process property, you can access more fine grained callbacks:

inquirer.prompt(prompts).ui.process.subscribe(onEachAnswer, onError, onComplete);
like image 638
William Bernting Avatar asked Nov 27 '19 21:11

William Bernting


1 Answers

So thanks to some inspiration from this blog post by jana e. beck, I made the code I needed. The examples and tips from jana was a little out of date, where using Subject from rxjs would no longer work for some reason, at least it did not work for me. This was easily fixed however by storing the emitter outside the Observer creation callback. Remember to add rxjs as a dependency to your project (same as the one InquirerJS is currently using probably helps).

const inquirer = require("inquirer");
var { Observable } = require("rxjs");

let emitter;

var prompts = Observable.create(function(e) {
  emitter = e;
  // need to start with at least one question here
  emitter.next({
    type: "list",
    name: "fruits",
    message: "What is your favorite fruit?",
    choices: [
      {
        name: "Banana"
      },
      {
        name: "Apple"
      },
      {
        name: "Pear"
      }
    ]
  });
});

let times = 0;

inquirer.prompt(prompts).ui.process.subscribe(
  q => {
    let dots = new Array(times).fill(".").join("");

    if (q.answer.toLowerCase() === "pear") {
      console.log("That's Great. I would never forget a Pear-eater.");
      emitter.complete();
    }

    emitter.next({
      type: "list",
      name: "fruits",
      message:
        "Sorry, what is your favorite fruit? I forgot, was it " +
        q.answer +
        ", or something else?",
      choices: [
        {
          name: "Uh, Banana.." + dots,
          value: "banana"
        },
        {
          name: "Uh, Apple.." + dots,
          value: "apple"
        },
        {
          name: "Pear!",
          value: "pear"
        }
      ]
    });

    times++;
  },
  error => {
    console.log("Hm, an error happened. Why?");
  },
  complete => {
    console.log("I think we are done now.");
  }
);

This prompt along with the blog post should be what you need to get started. Remember that you can queue up multiple questions in one go, should you want to.

When you are done, you put emitter.complete(); somewhere to end the prompt.

like image 181
William Bernting Avatar answered Sep 28 '22 04:09

William Bernting