Open j-cr opened 2 years ago
Just ran into this myself when trying to make a spinner UI component configurable. I'm not blocked on this since I'll probably just use it as a reason to break up my monolithic UI and use composition instead of configuration, but it does seem like these types should conform to tea.Model
in principle.
This would indeed be useful. In order to create a form which supports multiple input types, in an extensible manner, I've had to create a new interface in order to compensate for the method signatures not matching.
Just a note that if this is something you need right now you can go ahead and wrap existing components so that they satisfy tea.Model
. For example:
func (m myViewport) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
m.actualViewport, cmd = m.actualViewport.Update(msg)
return m
}
// ...and then also wrap Init and View.
Otherwise, in terms of idiomatic Go, the saying goes “accept interfaces, return structs.” While the gains here will be composability, the tradeoff will be additional assertions on returned tea.Model
s to assure that they are indeed the types we expect.
// Update a child model.
newModel, cmd := viewport.Update(msg)
if vp, ok := newModel.(viewport.Model); ok {
m.viewport = vp
}
That's not to say we're not in favor of the change, but something to bear in mind.
I came here wondering about the same thing. I ran into this issue in a text-based game I was working on. It has a Message
component, which is a message that is either a short part of a narrative or a prompt asking the user a question. It has a responseComponent
(which is a tea.Model
) that can either be a "Press Enter to continue" message or a text input component. It made a lot more sense to use the Bubbles text input component than to use my own, but there wasn't an easy way to fit it in there, since it doesn't implement the tea.Model
interface. I ended up writing a wrapper for the Bubbles text input component, as @meowgorithm suggested above, but it was definitely a nontrivial amount of code to have to write: https://github.com/colececil/the-floppy-disk-of-forbidden-creatures/blob/main/internal/ui/input.go.
Bubble Tea's architecture and examples seem to encourage composability, so it feels odd that the components in this library are not composable in this manner. I think the "accept interfaces, return structs" point is valid, but at the same time, it could be argued that tea.Model
itself breaks that principle, and thus any application using Bubble Tea is forced to break that principle. This makes me wonder if something could be done in Bubble Tea itself to improve this, such as the generics example @j-cr provided.
Rationale: I want to write a function that works with any kind of component. I'm not seeing how to achieve this cleanly, since components actually do not satisfy the
github.com/charmbracelet/bubbletea.Model
interface. Please correct me if I'm misunderstanding something; I'm totally new to bubbletea!Problem 1. Missing
Init
method. Affected components:Not affected: progress, stopwatch, timer, viewport.
Problem 2. The
Update
method returns concrete component'sModel
, which makes it incompatible withtea.Model
:Backwards compatibility aside, consider using a type parameter in declaration of
tea.Model
:This is still suboptimal for sure; alternatively
Update
could simply returntea.Model
which the user then would have to downcast back to component's specific model.