Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability for routing integrations to not cache views #19531

Closed
NiklasMerz opened this issue Oct 2, 2019 · 12 comments
Closed

feat: add ability for routing integrations to not cache views #19531

NiklasMerz opened this issue Oct 2, 2019 · 12 comments
Labels
package: angular @ionic/angular package package: react @ionic/react package package: vue @ionic/vue package type: feature request a new feature, enhancement, or improvement

Comments

@NiklasMerz
Copy link
Contributor

NiklasMerz commented Oct 2, 2019

Bug Report

Ionic version:

[x] 4.x

Current behavior:

It looks like the back button does not show up in some cases when it should. This happens if one page gets opened a second time.

Possibly pages get initialized without 'can-go-back' and this does not get updated.

Wrong behavior:
My app opens a "list page" which can open a "detail page" which can open the "list page" with other query params again. This "list page" does not show an ionic-back-button.

Expected behavior:

I would expect the ionic-back-button to show on the last "list page" and take me back to the "detail page" and from there another ionic-back-button to the first "list page".

Steps to reproduce:

  • Start app on home page
  • Click on button on homepage at the bottom "LIST"
  • Navigated to ListPage
  • Click on "Item 1"
  • Navigated to Homepage
    -> There is no back button

If I use the list page from menu, the back-button will show on the home page. But if I go to the list page again from there, the button will disappear as well.

Related code:

This is my test case.

Other information:

Ionic info:

New menu template

Ionic:

   Ionic CLI                     : 5.4.1 (/Users/gedysintraware/.nvm/versions/node/v10.13.0/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.10.0
   @angular-devkit/build-angular : 0.801.3
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.0.0

Utility:

   cordova-res : 0.6.0 
   native-run  : 0.2.8 

System:

   NodeJS : v10.13.0 (/Users/gedysintraware/.nvm/versions/node/v10.13.0/bin/node)
   npm    : 6.4.1
   OS     : macOS Mojave

@ionitron-bot ionitron-bot bot added the triage label Oct 2, 2019
@marcoromag
Copy link

same happens with React apps

NiklasMerz added a commit to NiklasMerz/ionic that referenced this issue Oct 4, 2019
@NiklasMerz
Copy link
Contributor Author

I added a test case for this to the Angular test app in this repro, which shows a diffrent flavor of this issue quite well. Would love to create a PR for that and I am looking for a fix!

Steps to reproduce with this test:

  • npm start in "angular/test/testapp"
  • Go to localhost:4200
  • Click "Router link test"
  • Click "Query params and fragment" at the bottom
  • Click "Go to page 3"
  • Click "Go to page 2 again...."

Now we are on page 2 and can see a back button. This is different from my repro app, but not correct, too. If I click on page I get redirected to /router-linkand not page 3 like expected.

@NiklasMerz
Copy link
Contributor Author

NiklasMerz commented Oct 4, 2019

My further debugging results.

Views are reused from the stack if they are navigated to the second time.

This line is responsible for losing the history after the first appearance of the view after using it the second time: https://github.com/ionic-team/ionic/blob/bfb704e2d2abf7a1e95e00e65206488c5a3558b7/angular/src/directives/navigation/stack-utils.ts#L24

Example for my theory

We navigate from 1 to 3 and the history looks like

  • Page 1
  • Page 2
  • Page 3

Now navigate to page 2 again after page 3 and the histoy looks like this

  • Page 1
  • Page 2

Because page 2 is reused the history rewinds to it's first appearance and I get back unexpectedly to my start point.

I debugged this with console.log in stack-controller:
Bildschirmfoto 2019-10-04 um 14 21 16

setActive(enteringView: RouteView): Promise<StackEvent> {
    console.debug("VIEW", this.views);
    let { direction, animation } = this.navCtrl.consumeTransition();
    const leavingView = this.activeView;
    const tabSwitch = isTabSwitch(enteringView, leavingView);
    if (tabSwitch) {
      direction = 'back';
      animation = undefined;
    }

    const viewsSnapshot = this.views.slice();

    let currentNavigation;

    const router = (this.router as any);

    // Angular >= 7.2.0
    if (router.getCurrentNavigation) {
      currentNavigation = router.getCurrentNavigation();

    // Angular < 7.2.0
    } else if (
      router.navigations &&
      router.navigations.value
    ) {
      currentNavigation = router.navigations.value;
    }

    /**
     * If the navigation action
     * sets `replaceUrl: true`
     * then we need to make sure
     * we remove the last item
     * from our views stack
     */
    if (
      currentNavigation &&
      currentNavigation.extras &&
      currentNavigation.extras.replaceUrl
    ) {
      if (this.views.length > 0) {
        this.views.splice(-1, 1);
      }
    }

    const reused = this.views.includes(enteringView);
    console.debug("!!REUSED!!!!!!!!!!", reused);
    const views = this.insertView(enteringView, direction);

    // Trigger change detection before transition starts
    // This will call ngOnInit() the first time too, just after the view
    // was attached to the dom, but BEFORE the transition starts
    if (!reused) {
      enteringView.ref.changeDetectorRef.detectChanges();
    }

    // Wait until previous transitions finish
    return this.zone.runOutsideAngular(() => {
      return this.wait(() => {
        // disconnect leaving page from change detection to
        // reduce jank during the page transition
        if (leavingView) {
          leavingView.ref.changeDetectorRef.detach();
        }
        // In case the enteringView is the same as the leavingPage we need to reattach()
        enteringView.ref.changeDetectorRef.reattach();
        console.debug("AFTER VIEWS", this.views);

        return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false)
          .then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
          .then(() => ({
            enteringView,
            direction,
            animation,
            tabSwitch
          }));
      });
    });
  }

I will update this post with new results to get this resolved quickly. Because of this bug our routing and navigation with back button is quite chaotic. Our app uses a few pages with different parameters very often in one navigation tree.

NiklasMerz added a commit to NiklasMerz/ionic that referenced this issue Oct 4, 2019
@NiklasMerz
Copy link
Contributor Author

I found a possible workaround!

Exisiting pages are reused if the URL matches. If I open an existing page a seconde time using a different URL it looks like the back button works as expected. Nevertheless there should be a fix for this. Since I'm not really sure how pages should be reused, I'm unsure how to fix this.

@NiklasMerz
Copy link
Contributor Author

Sorry for pushing this, but is this considered as a bug?

Now I have to do an ugly workaround and add to every page a garbage random paramter to avoid this caching issue. I would like to remove that as soon as possible.

@NiklasMerz
Copy link
Contributor Author

Sorry but bumping this again to prevent getting this locked.

@liamdebeasi liamdebeasi added the type: feature request a new feature, enhancement, or improvement label Oct 30, 2020
@ionitron-bot ionitron-bot bot removed the triage label Oct 30, 2020
@liamdebeasi liamdebeasi changed the title bug: <ion-back-button> does not appear if page is gets pushed to history the second time feat: add ability for routing integrations to not cache views Oct 30, 2020
@liamdebeasi
Copy link
Contributor

Thanks for the issue. By default, all of our routing integrations will re-use existing views if they match the same URL. For example:

/a --> /b --> /a would re-use the rendered view for /a and ion-back-button would not appear since it is on the root view.

While this is working as intended, I can understand that this may not be expected or desirable in some situations. Changing this would be a breaking change that we can look into, or we can look into making this an opt-in feature. Either way there is certainly going to need to be some discussion about how to implement this.

For anyone who is running into this scenario: Would you be able to provide a use case and a code sample that can help us understand how this behavior affects your applications? Thanks!

@liamdebeasi liamdebeasi added package: angular @ionic/angular package package: react @ionic/react package package: vue @ionic/vue package labels Oct 30, 2020
@HriBB
Copy link

HriBB commented Nov 20, 2020

I had a similar problem and solved it by defining multiple routes with similar path. Now when you go down the herarchy, new view is created each time.

export const View = () => (
  <IonReactRouter>
    <IonSplitPane contentId="main">
      <Menu />
      <IonRouterOutlet id="main">
        <Route path="/module" component={Module.List} exact />
        <Route path="/module/:f1" component={Module.List} exact />
        <Route path="/module/:f1/:f2" component={Module.List} exact />
        <Route path="/module/:f1/:f2/:f3" component={Module.List} exact />
        <Route component={NotFound} />
      </IonRouterOutlet>
    </IonSplitPane>
  </IonReactRouter>
);

I was surprised by this behavior at first, thought it was a bug in my app, but then I figured that Ionic must be doing some caching. After using the above setup, it works.

I think it's a cool feature, but needs to be mentioned more in the docs. Maybe with nice big bold font :)

@NoelDeMartin
Copy link

For anyone who is running into this scenario: Would you be able to provide a use case and a code sample that can help us understand how this behavior affects your applications? Thanks!

At Moodle we're migrating the mobile app from Ionic 3 to Ionic 5, and we've found that this breaks some navigation we relied upon before; in the fashion I outlined in #23455.

In particular, Moodle is an LMS (learning management system) with a lot of customization options. Given the nature of our software, there is a lot of interconnected pages. The specific scenario where we found this breaking down is navigating between pages in a course. Imagine that you are doing an activity linking to a section from another activity (a very common scenario). In this case, it is desirable to preserve the navigation stack in case you want to go back to the activity you came from.

If you want to look at the code you can find it here: https://github.com/moodlehq/moodleapp

@liamdebeasi
Copy link
Contributor

Hi there,

Thanks for filing the issue and providing more context around the various use cases. The team has decided not to implement this feature.

Caching views allow us to improve performance on lower end devices and add functionality such as swipe to go back on iOS. Removing this functionality would make it more difficult to have a performant swipe to go back gesture.

The use case in #19531 (comment) mentioned linking to other pages in a single course. This kind of pattern would benefit from parameterized URLs. By using parameterized URLs you can ensure that Ionic creates new instances of the same component whenever the parameters in the URL change.

For example, given a routing configuration of /a/:id, routes /a/1 and /a/2 would get two separate instances of the same component. We recommend using parameterized URLs when you need to create a new instance of a page.

I am going to close this. Thanks!

@NoelDeMartin
Copy link

NoelDeMartin commented Apr 13, 2022

@liamdebeasi Thanks for the update.

We are already using parametrized urls in our app, the problem is that as I mentioned in #23455 we can have pages linking to the exact same page that is already in the stack. For example, imagine that Wikipedia builds an Ionic app. If you start navigating between articles, you'll eventually end up in a page you already visited. But you wouldn't want to lose all the navigation history.

One solution would be to add a parameter that is a unique id in every route, which isn't related to the content of the page (maybe that's what you meant?). I guess that would work, but it'd be cumbersome to do that for every route in the app. And it doesn't make for pretty urls (although that's not a problem for us since we don't publish our app as a website).

In any case, thanks for letting us know about your decision. We'll try to find some workarounds in our side.

@ionitron-bot
Copy link

ionitron-bot bot commented May 13, 2022

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators May 13, 2022
@liamdebeasi liamdebeasi closed this as not planned Won't fix, can't repro, duplicate, stale Dec 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: angular @ionic/angular package package: react @ionic/react package package: vue @ionic/vue package type: feature request a new feature, enhancement, or improvement
Projects
None yet
Development

No branches or pull requests

5 participants