diff --git a/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts b/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts index 6abad6881b..1d1d1a5eba 100644 --- a/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts +++ b/src/Framework/Framework/Resources/Scripts/dotvvm-base.ts @@ -69,13 +69,19 @@ export function getStateManager(): StateManager { return getCoreS let initialViewModelWrapper: any; +function isBackForwardNavigation() { + return (performance.getEntriesByType?.("navigation").at(-1) as PerformanceNavigationTiming)?.type == "back_forward"; +} + export function initCore(culture: string): void { if (currentCoreState) { throw new Error("DotVVM is already loaded"); } // load the viewmodel - const thisViewModel = initialViewModelWrapper = JSON.parse(getViewModelStorageElement().value); + const thisViewModel = initialViewModelWrapper = + (isBackForwardNavigation() ? history.state?.viewModel : null) ?? + JSON.parse(getViewModelStorageElement().value); resourceLoader.registerResources(thisViewModel.renderedResources) @@ -124,8 +130,10 @@ const getViewModelStorageElement = () => document.getElementById("__dot_viewmodel_root") function persistViewModel() { - const viewModel = getState() - const persistedViewModel = { ...initialViewModelWrapper, viewModel }; - - getViewModelStorageElement().value = JSON.stringify(persistedViewModel); + history.replaceState({ + ...history.state, + viewModel: { ...initialViewModelWrapper, viewModel: getState() } + }, "") + // avoid storing the viewmodel hidden field, as Firefox would also reuse it on page reloads + getViewModelStorageElement()?.remove() } diff --git a/src/Samples/Tests/Tests/Complex/TaskListTests.cs b/src/Samples/Tests/Tests/Complex/TaskListTests.cs index 09fd44c01d..2e1776e050 100644 --- a/src/Samples/Tests/Tests/Complex/TaskListTests.cs +++ b/src/Samples/Tests/Tests/Complex/TaskListTests.cs @@ -62,5 +62,46 @@ public void Complex_TaskList_ServerRenderedTaskList() "Last task is not marked as completed."); }); } + + [Fact] + public void Complex_TaskList_TaskListAsyncCommands_ViewModelRestore() + { + // view model should be restored after back/forward navigation, but not on refresh + RunInAllBrowsers(browser => + { + browser.NavigateToUrl("/"); + browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_TaskList_TaskListAsyncCommands); + + browser.SendKeys("input[type=text]", "test1"); + browser.Click("input[type=button]"); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + + browser.NavigateBack(); + browser.WaitUntilDotvvmInited(); + browser.NavigateForward(); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + + browser.Refresh(); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); + + browser.SendKeys("input[type=text]", "test2"); + browser.Click("input[type=button]"); + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + + browser.NavigateToUrl("/"); + browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_TaskList_TaskListAsyncCommands); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); + + browser.NavigateBack(); + browser.WaitUntilDotvvmInited(); + browser.NavigateBack(); + + browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + }); + } } } diff --git a/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs b/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs index 79c468a479..7a47b04a0c 100644 --- a/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs +++ b/src/Samples/Tests/Tests/Feature/FormControlsEnabledTests.cs @@ -84,7 +84,7 @@ public void Feature_FormControlsEnabled_FormControlsEnabled() private void TestLinkButton(IBrowserWrapper browser, string id, bool shouldBeEnabled, ref int currentPresses) { - browser.First($"#{id}").Click(); + browser.First($"#{id}").ScrollTo().Wait(500).Click(); if (shouldBeEnabled) { currentPresses++;