Skip to content

Commit 04995df

Browse files
authored
Runs onload handler for browser navigations (backwards and forwards) (#1101)
1 parent 38edea8 commit 04995df

File tree

7 files changed

+93
-14
lines changed

7 files changed

+93
-14
lines changed

mesop/examples/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
boilerplate_free_event_handlers as boilerplate_free_event_handlers,
99
)
1010
from mesop.examples import box as box
11+
from mesop.examples import (
12+
browser_navigation_on_load as browser_navigation_on_load,
13+
)
1114
from mesop.examples import buttons as buttons
1215
from mesop.examples import checkbox_and_radio as checkbox_and_radio
1316
from mesop.examples import composite as composite
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import mesop as me
2+
3+
4+
@me.stateclass
5+
class State:
6+
page_1_onload_counter: int
7+
page_2_onload_counter: int
8+
9+
10+
def page_1_on_load(e: me.LoadEvent):
11+
me.state(State).page_1_onload_counter += 1
12+
13+
14+
@me.page(path="/browser_navigation_on_load/page1", on_load=page_1_on_load)
15+
def page1():
16+
me.text("page1")
17+
me.text(f"onload ran {me.state(State).page_1_onload_counter} times")
18+
me.button("navigate", on_click=navigate)
19+
20+
21+
def navigate(e: me.ClickEvent):
22+
me.navigate("/browser_navigation_on_load/page2")
23+
24+
25+
def page_2_on_load(e: me.LoadEvent):
26+
me.state(State).page_2_onload_counter += 1
27+
28+
29+
@me.page(path="/browser_navigation_on_load/page2", on_load=page_2_on_load)
30+
def page2():
31+
me.text("page2")
32+
me.text(f"onload ran {me.state(State).page_2_onload_counter} times")

mesop/protos/ui.proto

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ message QueryParam {
2424
repeated string values = 2;
2525
}
2626

27-
// Next ID: 19
27+
// Next ID: 20
2828
message UserEvent {
2929
optional States states = 1;
3030

@@ -43,6 +43,7 @@ message UserEvent {
4343
double double_value = 7;
4444
int32 int_value = 8;
4545
NavigationEvent navigation = 6;
46+
HotReloadEvent hot_reload = 19;
4647
ResizeEvent resize = 10;
4748
bytes bytes_value = 9;
4849
ChangePrefersColorScheme change_prefers_color_scheme = 14;
@@ -107,10 +108,14 @@ message DateRangePickerEvent {
107108
optional string end_date = 2;
108109
}
109110

110-
// This is a user-triggered navigation (e.g. go back/forwards) or a hot reload event.
111+
// This is a user-triggered navigation (e.g. go back/forwards).
111112
message NavigationEvent{
112113
}
113114

115+
// Fired when a hot reload is triggered.
116+
message HotReloadEvent {
117+
}
118+
114119
// Fired whenever a user resizes the viewport/browser.
115120
message ResizeEvent {
116121
}

mesop/runtime/context.py

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ def run_event_handler(
280280
) -> Generator[None, None, None]:
281281
if (
282282
event.HasField("navigation")
283+
or event.HasField("hot_reload")
283284
or event.HasField("resize")
284285
or event.HasField("change_prefers_color_scheme")
285286
):

mesop/server/server.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,18 @@ def generate_data(ui_request: pb.UiRequest) -> Generator[str, None, None]:
210210
runtime().context().reset_previous_node()
211211
runtime().context().reset_current_node()
212212

213-
result = runtime().context().run_event_handler(ui_request.user_event)
214213
path = ui_request.path
215214
has_run_navigate_on_load = False
215+
216+
if ui_request.user_event.HasField("navigation"):
217+
page_config = runtime().get_page_config(path=path)
218+
if (
219+
page_config and page_config.on_load and not has_run_navigate_on_load
220+
):
221+
has_run_navigate_on_load = True
222+
yield from run_page_load(path=path)
223+
224+
result = runtime().context().run_event_handler(ui_request.user_event)
216225
for _ in result:
217226
navigate_commands = [
218227
command
@@ -240,14 +249,7 @@ def generate_data(ui_request: pb.UiRequest) -> Generator[str, None, None]:
240249
and not has_run_navigate_on_load
241250
):
242251
has_run_navigate_on_load = True
243-
result = page_config.on_load(LoadEvent(path=path))
244-
# on_load is a generator function then we need to iterate through
245-
# the generator object.
246-
if result:
247-
for _ in result:
248-
yield from render_loop(path=path, init_request=True)
249-
runtime().context().set_previous_node_from_current_node()
250-
runtime().context().reset_current_node()
252+
yield from run_page_load(path=path)
251253

252254
yield from render_loop(path=path)
253255
runtime().context().set_previous_node_from_current_node()
@@ -265,6 +267,18 @@ def generate_data(ui_request: pb.UiRequest) -> Generator[str, None, None]:
265267
error=pb.ServerError(exception=str(e), traceback=format_traceback())
266268
)
267269

270+
def run_page_load(*, path: str):
271+
page_config = runtime().get_page_config(path=path)
272+
assert page_config and page_config.on_load
273+
result = page_config.on_load(LoadEvent(path=path))
274+
# on_load is a generator function then we need to iterate through
275+
# the generator object.
276+
if result:
277+
for _ in result:
278+
yield from render_loop(path=path, init_request=True)
279+
runtime().context().set_previous_node_from_current_node()
280+
runtime().context().reset_current_node()
281+
268282
@flask_app.route(UI_PATH, methods=["POST"])
269283
def ui_stream() -> Response:
270284
# Prevent CSRF by checking the request site matches the site
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {test, expect} from '@playwright/test';
2+
3+
test('browser navigation - back and forward (triggers onload)', async ({
4+
page,
5+
}) => {
6+
await page.goto('/browser_navigation_on_load/page1');
7+
await expect(page.getByText('page1')).toBeVisible();
8+
await expect(page.getByText('onload ran 1 times')).toBeVisible();
9+
10+
// Trigger a navigation.
11+
await page.getByRole('button', {name: 'navigate'}).click();
12+
13+
await expect(page.getByText('page2')).toBeVisible();
14+
await expect(page.getByText('onload ran 1 times')).toBeVisible();
15+
16+
// Go back to page 1
17+
await page.goBack();
18+
await expect(page.getByText('page1')).toBeVisible();
19+
await expect(page.getByText('onload ran 2 times')).toBeVisible();
20+
21+
// Go forward to page 2
22+
await page.goForward();
23+
await expect(page.getByText('page2')).toBeVisible();
24+
await expect(page.getByText('onload ran 2 times')).toBeVisible();
25+
});

mesop/web/src/services/channel.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
UserEvent,
88
Component as ComponentProto,
99
UiResponse,
10-
NavigationEvent,
1110
ComponentConfig,
1211
Command,
1312
ChangePrefersColorScheme,
13+
HotReloadEvent,
1414
} from 'mesop/mesop/protos/ui_jspb_proto_pb/mesop/protos/ui_pb';
1515
import {Logger} from '../dev_tools/services/logger';
1616
import {Title} from '@angular/platform-browser';
@@ -482,8 +482,7 @@ export class Channel {
482482
const request = new UiRequest();
483483
const userEvent = new UserEvent();
484484
userEvent.setStates(this.states);
485-
const navigationEvent = new NavigationEvent();
486-
userEvent.setNavigation(navigationEvent);
485+
userEvent.setHotReload(new HotReloadEvent());
487486
userEvent.setViewportSize(getViewportSize());
488487
userEvent.setThemeSettings(this.themeService.getThemeSettings());
489488
userEvent.setQueryParamsList(getQueryParams());

0 commit comments

Comments
 (0)