charmbracelet / charmbracelet/bubbles
Components do not satisfy tea.Model interface
- Dominant language
- Go
- Stars
- 8.9k
- Forks
- 457
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 5
Description
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:
- cursor
- help
- list
- paginator
- spinner
- textarea
- textinput
Not affected: progress, stopwatch, timer, viewport.
```
var m tea.Model = textinput.New()
// cannot use textinput.New() (value of type textinput.Model)
// as tea.Model value in variable declaration:
// missing method Init
```
---
**Problem 2.** The `Update` method returns concrete component's `Model`, which makes it incompatible with `tea.Model`:
```
var m tea.Model = timer.New(1 * time.Second)
// cannot use timer.New(1 * time.Second) (value of type timer.Model)
// as tea.Model value in variable declaration: wrong type for method Update (
// have func(msg github.com/charmbracelet/bubbletea.Msg) (github.com/charmbracelet/bubbles/timer.Model, github.com/charmbracelet/bubbletea.Cmd),
// want func(github.com/charmbracelet/bubbletea.Msg) (github.com/charmbracelet/bubbletea.Model, github.com/charmbracelet/bubbletea.Cmd))
```
Backwards compatibility aside, consider using a type parameter in declaration of `tea.Model`:
```
type MyModel[T any] interface {
Update() MyModel[T]
// skipped for brevity:
// Init() tea.Cmd
// Update(tea.Msg) (MyModel[T], tea.Cmd)
// View() string
}
type timerModel struct{}
type viewportModel struct{}
func (m timerModel) Update() MyModel[timerModel] { return m }
func (m viewportModel) Update() MyModel[viewportModel] { return m }
func updateModel[T any](m *MyModel[T]) {
// e.g.: updateModel(msg, &cmds, &m.timer) instead of:
// m.timer, cmd = m.timer.Update(msg); cmds = append(cmds, cmd)
// simplified for brevity
newM := (*m).Update()
*m = newM
}
func test() {
var x1 MyModel[timerModel] = timerModel{}
var x2 MyModel[viewportModel] = viewportModel{}
updateModel(&x1)
updateModel(&x2)
}
```
This is still suboptimal for sure; alternatively `Update` could simply return `tea.Model` which the user then would have to downcast back to component's specific model.
Contributor guide
Assessment
This issue has not been assessed yet.