Inquirer.js icon indicating copy to clipboard operation
Inquirer.js copied to clipboard

override like prompt?

Open tonylukasavage opened this issue 11 years ago • 7 comments

One bit of functionality missing from inquirer that I found indispensable in prompt is the ability to provide an override to the prompting process. So in the case of inquirer, if I have 2 questions I want to ask, but in previous code I've already determined the value of 1 of them, I'd love it if I could do something like this:

var questions = [
    {
        name: 'name',
        type: 'input',
        message: 'name:'
    },
    {
        name: 'value',
        type: 'input',
        message: 'value:'
    },
];
var override = {
    name: 'this is the name'
};
inquirer.prompt(questions, override, function(answers) {
    // process answers
});

In the above case, only the value question would be asked since name is already available in the override. The resulting answers object would include the input answer for value and the value from the override for name.

I didn't see this functionality in the docs nor did I see any means to do it in a very quick check of the source.

tonylukasavage avatar Sep 30 '14 15:09 tonylukasavage

There's a lot of way this can be done manually by the user in pretty easy way (build/filter the question array) - or even using parts of inquirer api like when functions.

It is actually a pretty common use case in a lot of yeoman-generators.

Thanks for the suggestion, but I don't think this functionality would add real benefits over filtering the array you pass to inquirer manually.

SBoudrias avatar Sep 30 '14 15:09 SBoudrias

can we reopen this?

I'll try to illustrate a good use case:

var inquirer = require('inquirer');
inquirer.registerPrompt(
  'autocomplete',
  require('inquirer-autocomplete-prompt')
);
...
inquirer.prompt([{
  type: 'autocomplete',
  name: 'from',
  message: 'Select a state to travel from',
  source: function(answersSoFar, input) {
    return myApi.searchStates(input);
  }
}]).then(function(answers) {
  //etc
});

If we filter the questions, you will see that answersSoFar will be missing what could be useful for narrowing down a search result, for example. I understand there are hacks around it, but I agree with @tonylukasavage that this is an important missing feature.

pyramation avatar Aug 25 '17 02:08 pyramation

Okay, I'll reopen. That's been a feature asked many times.

I'm still unsure how this could cleanly fit in the codebase - and it needs to works properly with plugins.

SBoudrias avatar Aug 25 '17 02:08 SBoudrias

cool! I took a stab at it here: https://github.com/pyramation/Inquirer.js/pull/1/files

I think I have something that works, but 3 tests are broken, maybe you can see what I did wrong! The test case I wrote passes---it's something around async fetching of property defaults that broke (error messages at bottom of this post)

My approach was to return an always-resolving answer from the overrides before asking the user anything.

PromptUI.prototype.fetchAnswer = function (question) {
  if (question.name && _.has(this.overrides, question.name)) {
    return rx.Observable.defer(
      function () {
        return rx.Observable.fromPromise(
          new Promise(resolve => {
            resolve({
              name: question.name,
              answer: this.overrides[question.name]
            });
          })
        );
      }.bind(this)
    );
  }

  var Prompt = this.prompts[question.type];
  this.activePrompt = new Prompt(question, this.rl, this.answers);
  return rx.Observable.defer(function () {
    return rx.Observable.fromPromise(this.activePrompt.run().then(function (answer) {
      return {name: question.name, answer: answer};
    }));
  }.bind(this));
};

For providing overrides, I passed them into the prompt constructor:

    var overrides = {
      q1: false
    };
    var prompts = [
      {
        type: 'confirm',
        name: 'q1',
        message: 'message',
        default: true
      },
      {
        type: 'confirm',
        name: 'q2',
        message: 'message',
        default: false
      }
    ];
    var promise = this.prompt(prompts, overrides);

Here were the errors:


  1) inquirer.prompt should run asynchronous `message`:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.


  2) inquirer.prompt should parse `default` if passed as a function:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.


  3) inquirer.prompt should parse `choices` if passed as a function:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

pyramation avatar Aug 25 '17 13:08 pyramation

finally got around to it - but ended up just making a wrapper. If anyone is interested in this https://github.com/pyramation/inquirerer

pyramation avatar Feb 28 '18 22:02 pyramation

What is the current state of this? @pyramation gave a solution by creating another package. Do we expect to implement this feature in this package as well? Should we close it?

@tonylukasavage you are the one who opened the issue. What is your opinion?

GeorgeGkas avatar Sep 16 '18 09:09 GeorgeGkas

I believe this use case is covered with the following:

const answers = require('./answers'); // This could be from a file, or from some business logic. 
const questions = require('./questions');

inquirer
    .prompt(questions, answers) // provide `answers` as an argument, and inquirer skips those questions
    .then((_answers) => {
      // Use user feedback for... whatever!!
      preFlightChecks(_answers);
    })
    .catch(handleError);

answers.js

module.exports = {
  description: "Default answer, or something specific",
  .
  .
  .
}

Answers will contain objects whose answers are known. When this is passed as args into Inquirer it skips those questions.

questions.js

module.exports =  [
  {
    type: "input",
    name: "title",
    message: "Title",
    default: "Random Title",
  },
  {
    type: "input",
    name: "description",
    message: "Description",
    when: function (answers) {
      return answers.title;
    },
  }

PsyGik avatar May 16 '21 05:05 PsyGik