howdy/ui/questionnaire
Questionnaires: questions asked one at a time, with progress, back, skip and next, each answer checked before moving on, and the answers gathered at the end.
let questions = [
questionnaire.question("role", "What do you do?", questionnaire.Single([
#("dev", "I build software"), #("design", "I design it"), #("other", "Something else"),
]))
|> questionnaire.required,
questionnaire.question("tools", "Which do you use?", questionnaire.Several([
#("gleam", "Gleam"), #("elixir", "Elixir"), #("erlang", "Erlang"),
])),
questionnaire.question("score", "How likely are you to recommend us?",
questionnaire.Scale(from: 0, to: 10, low: "Not likely", high: "Very likely")),
questionnaire.question("more", "Anything else?", questionnaire.Long),
]
// On a page: show the first question, then handle each post.
questionnaire.view(words.english(), questionnaire.start(questions), [attribute.method("post")])
use fields <- form.read(ctx)
let asked = questionnaire.submit(questions, form.fields(fields))
case questionnaire.is_complete(asked) {
True -> save(questionnaire.answers(asked))
False -> render(questionnaire.view(words.english(), asked, [attribute.method("post")]))
}
The form carries the answers so far and the current step in hidden
fields, so the server keeps nothing between posts. In a live view, pass
event.on_submit(Submitted) in the attributes and call submit with
the fields it gives you; the button pressed comes with them.
Next checks the current answer: a required question needs one, a
choice must be one of its options and a scale number on the scale, and
a check can reject it with a message. Past the last question every
answer is checked again, since earlier ones came back from the form,
and the first that fails is asked again. Back keeps what was typed, and
Skip, offered for questions that are not required, clears it.
view takes words for its buttons, its progress and the reasons an
answer is not accepted; see howdy/ui/words. A check’s own message
is shown as it is.
Types
How a question is answered.
pub type Kind {
Single(options: List(#(String, String)))
Several(options: List(#(String, String)))
Short
Long
Scale(from: Int, to: Int, low: String, high: String)
}
Constructors
-
Single(options: List(#(String, String)))One of these options, as
#(value, label). -
Several(options: List(#(String, String)))Any of these options.
-
ShortA line of text.
-
LongA paragraph of text.
-
Scale(from: Int, to: Int, low: String, high: String)A number on a scale, with words for its ends.
Where someone has got to.
pub opaque type Questionnaire
Values
pub fn actions_class() -> css.Class
pub fn answers(
state: Questionnaire,
) -> List(#(String, List(String)))
Every answer given, in the order the questions were asked. Questions skipped or left empty are left out.
pub fn check(
question: Question,
check: fn(List(String)) -> Result(Nil, String),
) -> Question
Check an answer before moving on; an Error holds the message to show.
pub fn count_class() -> css.Class
pub fn form_class() -> css.Class
pub fn help(question: Question, help: String) -> Question
A line under the question saying more about it.
pub fn is_complete(state: Questionnaire) -> Bool
Whether the last question has been answered or skipped.
pub fn options_class() -> css.Class
pub fn progress(state: Questionnaire) -> #(Int, Int)
The question being asked, from 1, and how many there are.
pub fn progress_class() -> css.Class
pub fn prompt_class() -> css.Class
pub fn question(
id: String,
prompt: String,
kind: Kind,
) -> Question
A question, answered in a field named id.
pub fn question_class() -> css.Class
pub fn scale_class() -> css.Class
pub fn scale_ends_class() -> css.Class
pub fn scale_step_class() -> css.Class
A number on the scale: a radio button drawn as a box, filled when chosen.
pub fn scale_steps_class() -> css.Class
pub fn start(questions: List(Question)) -> Questionnaire
The first question, with nothing answered.
pub fn submit(
questions: List(Question),
fields: List(#(String, String)),
) -> Questionnaire
Take a submitted form: restore the answers so far, read the current answer, and go back, skip, or check it and go on, as the button pressed asks.
pub fn view(
words: words.Words,
state: Questionnaire,
attributes: List(attribute.Attribute(msg)),
) -> element.Element(msg)
The current question as a form. attributes go on the <form>: a
method and action on a page, or a submit handler in a live view.