maxence-charriere / go-app

A package to build progressive web apps with Go programming language and WebAssembly.

Home Page:https://go-app.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

problem with page render

gmax79 opened this issue · comments

commented

Why does render not work correctly in this example?
Only one element is updated, although the entire page is recreated.
The error occurs if a line is added to the middle of the list (sorting)

type item struct {
	app.Compo
	name string
}

func (t *item) Render() app.UI {
	return app.Div().Body(
		app.Text(t.name),
	)
}

type form struct {
	app.Compo
	value string
}

func (f *form) Render() app.UI {
	return app.Div().Body(
		app.Input().Type("text").Value(f.value).OnChange(f.ValueTo(&f.value)),
		app.Button().Text("ADD").OnClick(
			func(ctx app.Context, e app.Event) {
				ctx.NewAction("update")
			},
		),
	)
}

type Test struct {
	app.Compo
	page app.UI

	Items []string
	form  *form
}

func (p *Test) OnMount(ctx app.Context) {
	ctx.Handle("update", func(app.Context, app.Action) {
		p.Items = append(p.Items, p.form.value)
		sort.Strings(p.Items)
		p.OnNav(ctx)
		p.Update()
	})

	p.Items = []string{"a", "b"}
	p.form = &form{}
}

func (p *Test) OnNav(ctx app.Context) {
	c := make([]app.UI, 0)
	for _, n := range p.Items {
		c = append(c, &item{name: n})
	}

	c = append(c, p.form)
	p.page = app.Div().Body(c...)
}

func (p *Test) Render() app.UI {
	if app.IsServer {
		return app.Text("")
	}
	if p.page == nil {
		return app.Text("")
	}

	return p.page
}

See this explanation: #672 (comment)

Changing the Name field to exported will cause the correct sort and update.

type item struct {
	app.Compo
	Name string
}

Calling OnNav from your code a bit unusual, that's typically called only from the go-app framework.

Here's your example code slightly re-written using action tags for passing the value
and removing OnNav, p.page, and p.form.

type item struct {
	app.Compo
	Name string
}

func (t *item) Render() app.UI {
	return app.Div().Body(
		app.Text(t.Name),
	)
}

type form struct {
	app.Compo
	value string
}

func (f *form) Render() app.UI {
	return app.Div().Body(
		app.Input().Type("text").Value(f.value).OnChange(f.ValueTo(&f.value)),
		app.Button().Text("ADD").OnClick(
			func(ctx app.Context, e app.Event) {
				ctx.NewAction("update", app.T("value", f.value))
			},
		),
	)
}

type Test struct {
	app.Compo
	Items []string
}

func (p *Test) OnMount(ctx app.Context) {
	p.Items = []string{"a", "b"}
	ctx.Handle("update", func(ctx app.Context, action app.Action) {
		p.Items = append(p.Items, action.Tags.Get("value"))
	})
}

func (p *Test) Render() app.UI {
	sort.Strings(p.Items)
	c := make([]app.UI, len(p.Items)+1)
	for i, s := range p.Items {
		c[i] = &item{Name: s}
	}
	c = append(c, &form{})
	return app.Div().Body(c...)
}
commented

@mlctrez Thanks for the help.
The reason why I do prerender in OnNav is because there is no context in Render function.
More complex pages often need context

We also do things in OnNav when we use the URL as route for the page for example.