node.jsinquirer

Using previous answer for the next question in inquirer


I want to use the previous answer for the next question in inquirer, and this is my code:

const promptForMissingOptions = async (options) => {
  const questions = [];

  if (!options.name) {
    questions.push({
      type: "input",
      name: "name",
      message: "Name:",
    });
  }

  if (!options.file) {
    questions.push({
      type: "checkbox",
      name: "lots",
      message: "Select the categories:",
      pageSize: 30,
      choices: () => {
        const files = helpers.getFileNames();
        return Object.keys(files);
      },
    });
  }

  if (answers.lots) {
    questions.push({
      type: "checkbox",
      name: "files",
      message: "Select the files:",
      pageSize: 50,
      choices: () => {
        const choices = helpers.getFileNames();
        return Object.keys(
          Object.keys(x)
            .filter((key) => answers.lots.includes(key))
            .reduce((obj, key) => {
              obj[key] = x[key];
              return obj;
            }, {})
        ).reduce(
          (r, k) =>
            r.concat(
              new inquirer.Separator(
                chalk.underline.bold.bgMagenta(
                  "------------------------ " + k + " ------------------------"
                )
              ),
              choices[k]
            ),
          []
        );
      },
    });
  }

  const answers = await inquirer.prompt(questions);

  return {
    files: options.file ? [options.file] : answers.files,
    name: options.name || answers.name,
  };
};


let options = await promptForMissingOptions(options);

What I want to do here is to use the answer from the second question for the third question.

I tried the solution in here : Is there a way to use previous answers in inquirer when presenting a prompt (inquirer v6)?

Which didn't work for my case.

How can I solve this ?

Thanks in advance.


Solution

  • Before I get to your question, I would like to point out some optimisations to your example code that would help with this:

    Where you currently using this:

    const questions = [];
    
    if (!options.name) {
      questions.push({ question });
    }
    

    You could achieve the same with:

    const questions = [{
      type: "input",
      name: "name",
      message: "Name:",
      when: () => options.name,
    }];
    

    Two points from the docs:

    when: (Function, Boolean) Receive the current user answers hash and should return true or false depending on whether or not this question should be asked. The value can also be a simple boolean.

    The wording here is slightly misleading, but what it boils down to here is that when the function you assign to when is called, it gets the current user answers passed to it as the first argument, and ultimately your function must return either true or false to determine if it should be shown to the user.

    choices: (Array|Function) Choices array or a function returning a choices array. If defined as a function, the first parameter will be the current inquirer session answers. [...]

    The description does go on to talk about possible answer value types, but the key takeaway here is the first function parameter: "the current inquirer session answers". At the point of Question 3, you have access to the answers from Question 2 in there.

    Also, just to throw in a little "personal opinion" here, I would highly recommend you move the logic that depends on Object.keys() out of your question choices handlers.. Rather pass in the resulting array of names to the question when you call it (i.e.: your current options argument). If any aspect of the structure changes, it will most likely do so somewhere else; if you keep this logic here, then all changes to that logic will require updates in both places in your code.


    Now, to apply this concept to your example questions array, I'll use an example applying the above concepts for brevity.

    const promptForMissingOptions = async (options) => {
      const questions = [
        {
          type: "input",
          name: "name",
          message: "Name:",
          when: () => options.name,
        },
        {
          type: "checkbox",
          name: "lots",
          message: "Select the categories:",
          when: () => options.file,
          pageSize: 30,
          choices: () => options.categoryChoices,
        },
        {
          type: "checkbox",
          name: "files",
          message: "Select the files:",
          when: () => answers.lots.length,
          pageSize: 50,
          choices: (answers) => {
            const filteredList = [];
            const selectedCategories = answers.lots; // where the ID is the relevant question's "name".
            // now do your "filter" handling here..
            return filteredList;
          },
        },
      ];
    
      const answers = await inquirer.prompt(questions);
    
      return {
        files: options.file ? [options.file] : answers.files,
        name: options.name || answers.name,
      };
    };
    
    let options = await promptForMissingOptions(options);
    

    I believe the code itself and the small comment in Question 3 should be clear enough here, but please do let me know whether or not this helps?