Partial Refresh with Portal
As said before, The results of an web.EventFunc
could be:
- Go to a new page
- Reload the whole current page
- Refresh part of the current page
We have covered two. Now let's demonstrate refresh part of the current page:
import (
"time"
"github.com/qor5/web/v3"
. "github.com/theplant/htmlgo"
)
func PartialUpdatePage(ctx *web.EventContext) (pr web.PageResponse, err error) {
pr.Body = Div(
H1("Partial Update"),
A().Text("Edit").Href("javascript:;").
Attr("@click", web.POST().EventFunc("edit1").Go()),
web.Portal(
If(len(fd.Title) > 0,
H1(fd.Title),
H5(fd.Date),
).Else(
Text("Default value"),
),
).Name("part1"),
Div().Text(time.Now().Format(time.RFC3339Nano)),
)
return
}
type formData struct {
Title string
Date string
}
var fd formData
func edit1(ctx *web.EventContext) (er web.EventResponse, err error) {
er.UpdatePortals = append(er.UpdatePortals, &web.PortalUpdate{
Name: "part1",
Body: Div(
web.Scope(
Fieldset(
Legend("Input value"),
Div(
Label("Title"),
Input("").Type("text").Attr("v-model", "form.Title"),
),
Div(
Label("Date"),
Input("").Type("date").Attr("v-model", "form.Date"),
),
),
Button("Update").
Attr("@click", web.POST().EventFunc("reload2").Go()),
).VSlot("{ locals, form }").FormInit(JSONString(fd)),
),
})
return
}
func reload2(ctx *web.EventContext) (er web.EventResponse, err error) {
ctx.MustUnmarshalForm(&fd)
er.Reload = true
return
}
var PartialUpdatePagePB = web.Page(PartialUpdatePage).
EventFunc("edit1", edit1).
EventFunc("reload2", reload2)
var PartialUpdatePagePath = URLPathByFunc(PartialUpdatePage)
web.Portal().Name("part1")
Place a placeholder inside you page, and append web.PortalUpdate
to er.UpdatePortals
to update the portal with that name.
Multiple portal can be updated at the same time.
Load Portal in separate AJAX request
With web.Portal
, We can also load the portal with a separate AJAX request after page load.
It is useful for the type of the content is not that important to the page, But load them are
quite heavy. Like related products of a product detail page of a ECommerce site.
import (
"fmt"
"time"
"github.com/qor5/web/v3"
. "github.com/theplant/htmlgo"
)
func PartialReloadPage(ctx *web.EventContext) (pr web.PageResponse, err error) {
reloadCount = 0
ctx.Injector.HeadHTML(`
<style>
.rp {
float: left;
width: 200px;
height: 200px;
margin-right: 20px;
background-color: orange;
}
</style>
`,
)
pr.Body = Div(
H1("Portal Reload Automatically"),
web.Scope(
web.Portal().Loader(web.POST().EventFunc("autoReload")).AutoReloadInterval("locals.interval"),
Button("stop").Attr("@click", "locals.interval = 0"),
).Init(`{interval: 2000}`).VSlot("{ locals, form }"),
H1("Load Data Only"),
web.Scope(
Ul(
Li(
Text("{{item}}"),
).Attr("v-for", "item in locals.items"),
),
Button("Fetch Data").Attr("@click", web.GET().EventFunc("loadData").ThenScript(`locals.items = r.data`).Go()),
).VSlot("{ locals, form }").FormInit("{ items: []}"),
H1("Partial Load and Reload"),
Div(
H2("Product 1"),
).Style("height: 200px; background-color: grey;"),
H2("Related Products"),
web.Portal().Name("related_products").Loader(web.POST().EventFunc("related").Query("productCode", "AH123")),
A().Href("javascript:;").Text("Reload Related Products").
Attr("@click", web.POST().EventFunc("reload3").Go()),
)
return
}
func related(ctx *web.EventContext) (er web.EventResponse, err error) {
code := ctx.R.FormValue("productCode")
er.Body = Div(
Div(
H3("Product A (related products of "+code+")"),
Div().Text(time.Now().Format(time.RFC3339Nano)),
).Class("rp"),
Div(
H3("Product B"),
Div().Text(time.Now().Format(time.RFC3339Nano)),
).Class("rp"),
Div(
H3("Product C"),
Div().Text(time.Now().Format(time.RFC3339Nano)),
).Class("rp"),
)
return
}
func reload3(ctx *web.EventContext) (er web.EventResponse, err error) {
er.ReloadPortals = []string{"related_products"}
return
}
var reloadCount = 1
func autoReload(ctx *web.EventContext) (er web.EventResponse, err error) {
er.Body = Span(time.Now().String())
reloadCount++
if reloadCount > 5 {
er.RunScript = `locals.interval = 0;`
}
return
}
func loadData(ctx *web.EventContext) (er web.EventResponse, err error) {
var r []string
for i := 0; i < 10; i++ {
r = append(r, fmt.Sprintf("%d-%d", i, time.Now().Nanosecond()))
}
er.Data = r
return
}
var PartialReloadPagePB = web.Page(PartialReloadPage).
EventFunc("related", related).
EventFunc("reload3", reload3).
EventFunc("autoReload", autoReload).
EventFunc("loadData", loadData)
var PartialReloadPagePath = URLPathByFunc(PartialReloadPage)
It is not only load the portal in separate AJAX request, Also you can reload it with ease er.ReloadPortals = []string{"related_products"}
in an event func.
Under the hood, We use Vue's Dynamic & Async Components, to load Go generated html (vue runtime templates) from the server and mount those vue components into the page. It works the same way for reload the whole page, push state page switch, and refresh part of the current page.