AlecAivazis / survey

A golang library for building interactive and accessible prompts with full support for windows and posix terminals.
MIT License
4.08k stars 351 forks source link

Allow questions to set defaults from previously answered questions in the same set of questions #286

Closed rdmulford closed 2 years ago

rdmulford commented 4 years ago

When defining multiple questions in one block, it would be great if you could set the default value for one question equal to the answer of a previously answered question. See example below for more clarity on the issue.

Environments Tested

OS: MacOS Terminal: iTerm2 Shell: zsh Version: github.com/AlecAivazis/survey/v2 v2.0.7

Example Program:

package main

import (
    "github.com/AlecAivazis/survey/v2"
)

type Answers struct {
    Text      string
    Default   string
}

func main() {
    answers := Answers{}
    var questions = []*survey.Question{
        {
            Name:     "Default",
            Prompt:   &survey.Input{Message: "Enter text"},
            Validate: survey.Required,
        },
        {
            Name:     "Default",
            Prompt:   &survey.Input{
                Message: "Enter text (should be default here)",
                Default: answers.Default,
            },
            Validate: survey.Required,
        },
    }
    if err := survey.Ask(questions, &answers); err != nil {
        panic(err)
    }
}

Output

Screen Shot 2020-04-02 at 2 00 17 PM

Expected

I would expect the second question to display a default value of text.

kke commented 4 years ago

I think if answers already has a valid value for the key, asking for one should be skipped.

Something like this should go straight through without any prompts, the name should become titlecased.

    name := "hello world"
    email := "hello@example.com"

    userConfig := config.UserConfig{
        Name:    name,
        Email:   email,
    }

    questions := []*survey.Question{
        {
            Name:      "name",
            Prompt:    &survey.Input{Message: "Name"},
            Validate:  validateName,
            Transform: survey.Title,
        },
        {
            Name:     "email",
            Prompt:   &survey.Input{Message: "Email"},
            Validate: validateEmail,
        },
    }
    err := survey.Ask(questions, &userConfig)
infalmo commented 3 years ago

I think if answers already has a valid value for the key, asking for one should be skipped.

Define valid value. nil, 0, "" are all valid values in golang.

joerodrig commented 3 years ago

I stumbled across this case earlier when working on a CLI that I wanted to have some power user flags. For example, something like: ./cli create --name MyInput could help with cases where you would normally be prompted to enter a name.

My immediate thought was maybe adding a check before the prompt that checks if there's already an answer for the question, and if that answer passes validations, to skip the prompt portion. But then I can see how that opens up a whole other class of questions -- ie. How would a validation failure look? What if you just want pre-filled answers to be suggested defaults? What if you want them to skip validations?

Ultimately, it felt like I was trying to shoehorn my own business logic into the package, so I ended up managing those inputs on my end.

Simple Example:

var name string

func main() {
    ...
    if len(name) > 0 {
        fmt.Println(fmt.Sprintf("Name: %s", name))
        answers.Name = name
    ...
    } else {
        // questionsWithoutName is the array of survey.Question structs, with the Name question filtered out
    err := survey.Ask(questionsWithoutName, &answers)
    ...
    }
}

If you're running validations or transformations, things are likely a bit more complicated, but I was just working on something tiny, so this worked well enough.

montanaflynn commented 3 years ago

I also want to use a default and suggestions based on previous answers.

This would be very welcome, the syntax suggested seems reasonable.

mislav commented 2 years ago

Thanks everyone for the suggestions!

If your application needs subsequent questions to react to the answers of previous questions, you should go with multiple Asks:

if err := survey.Ask(questions1, &answers); err != nil {
  panic(err)
}
if err := survey.Ask(questions2, &answers); err != nil {
  panic(err)
}

That way you have absolute freedom to implement whichever logic you'd like.

Note that the Default property of Survey inputs is a Go value, not a reference. The provided value will be static and cannot change dynamically between survey prompts.