Replies: 6 comments 7 replies
-
Hi there! Curious, what's your use case? Also, assuming you meant this, right? func CmdFunc(msg tea.Msg) tea.Cmd {
return func() tea.Msg { return msg }
} |
Beta Was this translation helpful? Give feedback.
-
@meowgorithm for my first application, my main model contains a stack of models. The idea is that // Update takes this model and push it on the stack
type OpenMsg tea.Model
// Update removes topmost model from the stack
type CloseMsg struct{}
// some model Foo
func (f Foo) Update(tea.Msg) (tea.Model, tea.Cmd) {
// some condition where we want to open model Bar
bar := OpenMsg(Bar{}) // my new model to display
return CmdFunc(bar) // send the model back to the Update loop
} As I find myself turning a tea.Model into a tea.Cmd, I thought it would be useful to have this helper function. Now, maybe I'm approaching the problem the wrong way, and I shouldn't have to do that ... In which case I'm eager to be corrected! :) |
Beta Was this translation helpful? Give feedback.
-
So logic dictates that you shouldn't need to use a That said, while this is probably fine to use in practicality (we see it every so often) it does incur an unnecessary loop through the runtime. Because of that, we'd prefer not to include it in the core. In other words, it's techincally an antipattern, and actually a common one seen in The Elm Architecture, the design upon which Bubble Tea is built. |
Beta Was this translation helpful? Give feedback.
-
Thanks for your input! I think I oversimplified my example, and in that case // main model
type Main struct {
stack []tea.Model
}
// first sub-model
type Screen1 struct {}
// second sub-model
type Screen2 struct{} In Now, Let's say Or am I creating components, and I should avoid doing that, as ELM's documentation suggests? |
Beta Was this translation helpful? Give feedback.
-
Hey @tgirod when you say data I'm assuming you mean |
Beta Was this translation helpful? Give feedback.
-
I wonder if this question is actually about how to handle the common "data down, events up" pattern that we see in reactive systems like Elm and React. Consider the simplified example below -- it is a simple program with a bubbles textinput and an output component which capitalizes the user's input when they hit ENTER. We have a textinput model and an output model, and a top model with an event handler that listens to a subset of keys and passes the rest to the children models. What is the idiomatic pattern for sending an event from The only elegant solution I see is to use @tgirod's CmdFunc-like method to generate a message for the top-level Update handler. Any other pattern requires global state -or- a top-level Update handler that understands the state of all child components. Is there a cleaner solution? Thanks. // event generated by input component
type inputValueChanged string
// update handler for input component
func (m myInput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyEnter:
log.Printf("Value: %s", m.textInput.Value())
return m, func() tea.Msg { return inputValueChanged(m.textInput.Value()) } // <--- is this idiomatic?
}
}
var cmd tea.Cmd
m.textInput, cmd = m.textInput.Update(msg)
return m, cmd
}
func (m myInput) View() string {
return m.textInput.View()
}
// output component
type myOutput string
// listens for inputValueChanged events
func (m myOutput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case inputValueChanged:
m = myOutput(strings.ToUpper(string(msg)))
}
return m, nil
}
func (m myOutput) View() string {
return string(m)
}
// top-level model listens only for quit commands
func (m topModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyEsc, tea.KeyCtrlC:
return m, tea.Quit
}
}
var cmds []tea.Cmd
var cmd tea.Cmd
m.inputModel, cmd = m.inputModel.Update(msg)
if cmd != nil {
cmds = append(cmds, cmd)
}
m.outputModel, cmd = m.outputModel.Update(msg)
if cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
} Full .go filepackage main
import (
"fmt"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"log"
"strings"
)
type (
errMsg error
)
type inputValueChanged string
type myInput struct {
textInput textinput.Model
err error
}
func initialMyInput() tea.Model {
ti := textinput.New()
ti.Placeholder = "Foo"
ti.Focus()
return myInput{
textInput: ti,
err: nil,
}
}
func (m myInput) Init() tea.Cmd {
return textinput.Blink
}
func (m myInput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyEnter:
log.Printf("Value: %s", m.textInput.Value())
return m, func() tea.Msg { return inputValueChanged(m.textInput.Value()) }
}
case errMsg:
panic(msg)
}
var cmd tea.Cmd
m.textInput, cmd = m.textInput.Update(msg)
return m, cmd
}
func (m myInput) View() string {
return m.textInput.View()
}
type myOutput string
func (m myOutput) Init() tea.Cmd {
return nil
}
func (m myOutput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case inputValueChanged:
m = myOutput(strings.ToUpper(string(msg)))
}
return m, nil
}
func (m myOutput) View() string {
return string(m)
}
type topModel struct {
inputModel tea.Model
outputModel tea.Model
}
func initialTopModel() topModel {
return topModel{
inputModel: initialMyInput(),
outputModel: myOutput(""),
}
}
func (m topModel) Init() tea.Cmd {
return tea.Batch(m.inputModel.Init(), m.outputModel.Init())
}
func (m topModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyEsc, tea.KeyCtrlC:
return m, tea.Quit
}
}
var cmds []tea.Cmd
var cmd tea.Cmd
m.inputModel, cmd = m.inputModel.Update(msg)
if cmd != nil {
cmds = append(cmds, cmd)
}
m.outputModel, cmd = m.outputModel.Update(msg)
if cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}
func (m topModel) View() string {
return fmt.Sprintf("\n\nInput: %s\n\nOutput: %s\n\n", m.inputModel.View(), m.outputModel.View())
}
func main() {
f, err := tea.LogToFile("debug.log", "debug")
if err != nil {
panic(err)
}
defer f.Close()
topModel := initialTopModel()
p := tea.NewProgram(topModel)
if _, err := p.Run(); err != nil {
panic(err)
}
} |
Beta Was this translation helpful? Give feedback.
-
Here is a little helper function that I would find useful to have in bubbletea:
It is very similar to
http.HandlerFunc
and serves a similar purpose, I think.Beta Was this translation helpful? Give feedback.
All reactions