inertia
inertia copied to clipboard
Scroll regions inside page component are not restored after form submit.
Versions:
-
@inertiajs/inertia
version: 0.11.0 -
@inertiajs/inertia-vue3
version: 0.6.0
Describe the problem:
When a page, for example, an edit form contains a scroll-region
and we use either the form helper or Inertia.post
with { preserveScroll: true, preserveState: false }
, and then server redirects back to the form after a successful save, the scroll-region scroll position is reset, instead of being preserved.
When the scroll-region
is outside the page component (in the Layout
or the blade template, for example), it works as expected - the scroll position is maintained. This is because neither the layout nor the blade template get re-rendered.
Note that the issue only happens when using preserveState
with either false
or 'error'
. This is natural, because when the state is preserved, there are no DOM changes. However, when the state is refreshed from the server, the component is re-mounted, DOM changes and this resets the scroll position.
Inertia has a restoreScrollPositions()
method, that seems like is built for restoring scroll positions inside scroll regions. However, this is never called after making a form submit.
What happens is that when the request is successful, setPage
is called with the correct preserveScroll
flag, but setPage
itself only uses this flag to reset scroll position to the top of the page, if preserveScoll: false
.
This relies on the fact that if the document body is scrollable, then browsers would maintain the scroll position. However, browsers will not maintain scroll positions of the individual scroll regions inside the page component.
It seems to me that restoreScrollPositions()
should be called somewhere in the setPage
method, after the swapComponent
resolves.
Steps to reproduce:
- Create a
scroll-region
inside a page that has a form (such as an edit page) - Ensure that when the form submits, it uses the
{ preserveState: 'errors', preserveScroll: true }
options. - Ensure the server redirects back to the same page
- Scroll the
scroll-region
inside the page - Submit the form
- Notice how the scroll position inside the
scroll-region
is reset.
For easier reproduction, I've opened a PR on my fork with exact steps: https://github.com/ragulka/pingcrm/pull/1
Note: this issue may be related to https://github.com/inertiajs/inertia/issues/1181
I think I have the same issue. Clicking a link inside a scroll-region, with preserveScroll, doesn't preserve the scroll position after the new page is loaded, even though the scroll-region is also on the new page (it's a sidebar).
Hey! Thanks so much for your interest in Inertia.js and for sharing this issue/suggestion.
In an attempt to get on top of the issues and pull requests on this project I am going through all the older issues and PRs and closing them, as there's a decent chance that they have since been resolved or are simply not relevant any longer. My hope is that with a "clean slate" me and the other project maintainers will be able to better keep on top of issues and PRs moving forward.
Of course there's a chance that this issue is still relevant, and if that's the case feel free to simply submit a new issue. The only thing I ask is that you please include a super minimal reproduction of the issue as a Git repo. This makes it much easier for us to reproduce things on our end and ultimately fix it.
Really not trying to be dismissive here, I just need to find a way to get this project back into a state that I am able to maintain it. Hope that makes sense! ❤️
I'm having this issue is well. scroll-region
seems to be useless when used inside of a Vue component (for the reasons thoroughly described above). I'm surprised, because I would assume the test case would be using it inside of a Vue component. Also it's a shame this got closed out, because this is very much still an issue! @reinink
@troygilbert As per reininks comment, you can simply open a new ticket and include the requirements as described.
Hey happy to just reopen this one if it's still a known issue 👍
I'm experiencing the same issue
Same here, I have to add preserveState to get it work.
Experiencing the same issue here. It just doesnt seem to work within Vue components.
Exactly the same issue here,
I also noticed that when settings for example a div with overflow, with scroll-region
, and we console.log()
the page
element after onSuccess()
method, like so :
form.post('some/route/', {
onSuccess: page => { console.log(page) }
})
The array scrollRegions
is empty in the browser's inspector (see screenshot below)
This is still happening and I am thinking of a workaround.
Hello there, I have started to investigate this "issue" (is it really an issue ?), and this is what I found :
In packages/core/src/router.ts
, we have method setPage
like so :
protected setPage(
page: Page,
{
visitId = this.createVisitId(),
replace = false,
preserveScroll = false,
preserveState = false,
}: {
visitId?: VisitId
replace?: boolean
preserveScroll?: PreserveStateOption
preserveState?: PreserveStateOption
} = {},
): Promise<void> {
return Promise.resolve(this.resolveComponent(page.component)).then((component) => {
if (visitId === this.visitId) {
// console.log(page.scrollRegions) is always undefined at this stage
// with this.page, it work
page.scrollRegions = this.page.scrollRegions || []
page.rememberedState = page.rememberedState || {}
replace = replace || hrefToUrl(page.url).href === window.location.href
replace ? this.replaceState(page) : this.pushState(page)
this.swapComponent({ component, page, preserveState }).then(() => {
if (!preserveScroll) {
this.resetScrollPositions()
} else {
// I added this line here
this.restoreScrollPositions();
}
if (!replace) {
fireNavigateEvent(page)
}
})
}
})
}
As you can see, I have added a comment where I did some modification. Like this, it works, the scroll-region
is well-preserved on an element that have overflow set to scroll (tested on Vue3 playground).
But I have some questions, obviously I can't make a PR, because I "cheat" with this.page
instead of page
. If I don't do that, page.scrollRegions
is always undefined, even with scroll-region
and a scroll event.
- Why, in Promise,
console.log(page.scrollREgions)
is always undefined ? I think it's because it was defined asynchronously elsewhere, but where ?
If you guys have some idea, or advice, of course, feel free to tell me. I will continue to investigate this issue, but maybe this can already be useful.
The repos with playground set with this code is available on my GitHub account
Hello there,
I have made a npm module with this modification, for testing purpose on a real project.
If you want to give it a try : npm install @inertiajs-fix-scroll/vue3
(for vue3 packages).
Then, change all occurrences of @inertiajs/vue3
to @inertiajs-fix-scroll/vue3
in your project.
For me, it works with Svelte, Vue3, Vue2. I can't test it with React, because I have a lot of bugs with the React playgrounds, and I don't really investigate why.
Again, it's just for testing purpose
Has anyone been able to successfully work around this issue for a production site?
Has anyone been able to successfully work around this issue for a production site?
I finally chose to don't use Inertia for local dynamic refresh ('ajax'), and fetch data with native javascript fetch()
API and a regular Laravel route.
You can see demo here
And code example here (with axios but fetch()
native javaScript API is better imo)
After a lot of research and try, it's the simplest solution I have found
Still having this issue as well. Issue where i put the scroll-region on an overflow element but still the scrollbar reset.