Description
Bug Report
Ionic Info
Ionic:
ionic (Ionic CLI) : 4.5.0 (/usr/local/lib/node_modules/ionic)
Ionic Framework : @ionic/angular 4.0.0-beta.19
@angular-devkit/build-angular : 0.10.7
@angular-devkit/schematics : 7.0.7
@angular/cli : 7.0.7
@ionic/angular-toolkit : 1.2.0
System:
NodeJS : v10.13.0 (/usr/local/bin/node)
npm : 6.4.1
OS : macOS Mojave
Describe the Bug
(There are arguably two interrelated but separate issues here. If I should create separate issues then please let me know! 🙂)
Forward navigation to a Page
with the same URL (excluding query parameters) will modify the navigation stack history (not the browser history stack but the StackController
stack, which are different). It will then re-use a previous Page
instance that does not contain the latest navigation data from the forward navigation to the same URL.
Steps to Reproduce
Steps to reproduce the behavior:
- Clone this repo on your machine: https://github.com/KevinKelchen/ionic4-stack-page-reuse . It is a slightly modified version of the
sidemenu
Angular stater template. - In the terminal, path into the repo root and run
npm i
. - Run
ionic serve
. - Click the Menu icon and then
List
. - Click
Item 1
to forward navigate to the detail screen forItem 1
.- You will notice there's a timestamp that displays. I chose a timestamp in this example so that there's an easy way to see on-screen what navigation data is being used.
- This timestamp data was stored in a shared service prior to navigating from the source
Page
. It was then retrieved from the targetPage
from the same service. This pattern was outlined by Josh Morony here. The data in the service is keyed in a dictionary using thenavId
UUID URL query parameter. - A shared service is being used because we are migrating from Ionic 3 and the use of
NavParams
which allowed passing complex data like callback functions and objects which cannot be serialized in a lossless way in the URL or@ionic/storage
. - A UUID was chosen so that navigating to what's otherwise the same URL will use the navigation parameters specific to the current navigation. This will also preserve previous navigation parameters so they can be used when revisiting a
Page
in the browser history.
- Click the pencil/create button in the header. This will navigate you to a different Page type called
List Detail More
. - From this Page, click
Item 1
to forward navigate to the detail screen forItem 1
. Note that the timestamp is the old value that was displayed when you initially navigated toItem 1
. This is because theStackController
rolled back the stack and re-used the old instance of thePage
forItem 1
. The newnavId
URL query parameter was not used to retrieve the data from the shared service and thus is not using fresh data on this forward navigation. - To observe the
StackController
navigation stack modification, click theion-back-button
and it will take you to the initialList
Page
instead ofList Detail More
which is the screen you were previously on before revisitingItem 1
.
Related Code
The steps to reproduce above link to this repository which is set up to reproduce the issue.
Expected Behavior
I expected forward navigation to a Page
with the same URL to use the latest, current navigation data from the forward navigation to the same URL and not modify the StackController
navigation stack history.
Additional Context
Here is some additional context from a message I posted in #16367:
Note: My definition of "reusing" a Page is when "forward" navigating to a URL and instead of pushing a new Page onto the StackController
's "stack" it re-uses an instance of the Page that is already present in the stack.
I've been having issues with StackController
as well but on Beta 15. I am using the Angular router imperatively in TypeScript but I believe I was experiencing them as well using the new NavController
(which uses the Angular router).
Here's a related behavior I've observed:
When you use the router to "forward" navigate to a Page, if the URL (excluding query parameters) is the same as a Page that is already in the "stack," the StackController
will remove all of the previous Pages in the stack back to the first occurrence of the matching URL/Page. It then re-uses that first occurrence of the Page in the stack. And so then when you use ion-back-button
, which seems to ignore browser history and only uses the stack, the Page you go back to is not what you would expect--it's the Page prior to the first occurrence of the Page in the stack.
- Note: Since the Android and browser back button seems to ignore the
StackController
's "stack," I've tried working around this issue to some success by creating my own custom back button component that just uses Angular'sLocation.back()
when you click it. This tends to result in a lot of destroyed/re-initializing of Pages whose state you can attempt to preserve by storing off data in a shared service that is keyed with a unique ID (such as a UUID) present in the URL.
How StackController
re-uses Pages has actually been tripping me up in another way:
If I use the Angular router to "forward" navigate to a Page with the same URL as one in the StackController
's stack and I have navigation parameters that I store in a shared service (as opposed to the URL) and key the parameter storage in the service off of a URL query parameter such as a UUID (a query parameter--not using the matrix URL notation as noted at the end of this section), because the Page is being re-used due to a matching URL instead of being considered as different, the new query parameter value is not being used and the Page pulls the previous parameters out of the shared service due to using the old query parameter value.
Perhaps the solve is to work around it by just putting the UUID in as a matrix URL notation parameter and effectively opt-out of any StackController
reuse of Pages.
- Upsides
- "Forward" navigating to the same Page/URL would use the correct parameters.
- With all of the Pages in the stack remaining wired up (never destroyed or reused), when you navigate back to them they are exactly the way you left them when you navigated away, which is how Ionic 3 worked.
- Downsides
- It's non-default behavior that differs from how Ionic seems to think it should work in Ionic 4.
- Although it's like Ionic 3, you lose an optimization in which all Pages stay wired-up in the DOM as you continue to forward navigate through the app which may potentially increase the amount of memory that's used by the app; I would have concerns of how this might scale as users keep forward navigating through the app.
Thank you for all that you do and for making a great framework! 🙂