A value resets on the next request
A static page is built afresh for every request, so a field in the component starts over each time. Keep what the page needs in the address, or post it.
Problem
A value set in one request is gone in the next: a count goes back to its first value, a chosen step resets, a list loaded on the first visit is loaded again. Here every post shows 3 guests, however many times it's pressed; on an interactive page the same code counts up.
<form method="post" @formname="guests" @onsubmit="() => _guests++">
<AntiforgeryToken />
<Button Submit>Add a guest</Button>
</form>
<TextBlock>_guests guests</TextBlock>
{
private int _guests = 2;
}Cause
On an interactive page a component instance lives as long as it stays on the page, so its fields keep their values. On a static page, each request (a link followed, a form posted, a reload) creates new components, renders them and throws them away. Nothing is kept on the server between requests unless you store it.
Fix
Keep the state where the next request carries it:
- In the address. Read it with
[SupplyParameterFromQuery], and make each change a link that sets it withNavigationManager.GetUriWithQueryParameter, which keeps the rest of the query string. It survives a reload, and the page can be shared or bookmarked as it is. - In the form. Fields post their values, and the page reads them with
[SupplyParameterFromForm]. A value with no field of its own can post in a hidden<input>. - Elsewhere. A database on the server, or a cookie in the browser for a reader's preferences, read on each request.
NavigationManager Navigation
<Button Href="Navigation.GetUriWithQueryParameter("guests", Guests + 1)">
Add a guest
</Button>
<TextBlock>Guests guests</TextBlock>
{
[SupplyParameterFromQuery(Name = "guests")]
private int? GuestsQuery { get; set; }
private int Guests => GuestsQuery ?? 2;
}Code
NavigationManager Navigation
<Row Gap="3" Align="Align.Center">
<IconButton Icon="Lucide.Minus" Label="Fewer guests" Href="To(Guests - 1)" Disabled="(Guests <= Min)"
Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
<TextBlock Weight="FontWeight.Medium">Guests (Guests == 1 ? "guest" : "guests")</TextBlock>
<IconButton Icon="Lucide.Plus" Label="More guests" Href="To(Guests + 1)" Disabled="(Guests >= Max)"
Variant="ButtonVariant.Outline" Accent="Accent.Neutral" />
</Row>
{
private const int Min = 1;
private const int Max = 8;
// The count lives in the address (?guests=3), so each request knows it. A field would start
// over at every request on a static page.
[SupplyParameterFromQuery(Name = "guests")]
private int? GuestsQuery { get; set; }
private int Guests => Math.Clamp(GuestsQuery ?? 2, Min, Max);
private string To(int guests) => Navigation.GetUriWithQueryParameter("guests", guests);
}Off: Each button is a link to this page with a new ?guests=, so every request knows the count.
IzzyBlazor's components use the address the same way: Table, Pagination and
FilterForm keep their state in the query string, Tabs with a QueryKey
opens the tab the address names, and a dialog opens from ?dialog=id.
Related
- Render modes: state on static pages.
- OnClick does nothing on a static page: links and posts instead of events.
- Table: sorting, filters and pages in the address.