Skip to content

bug: vanilla js router, navigating back from route with multiple params causes url to not be updated #23435

@cjorasch

Description

@cjorasch

Bug Report

Ionic version:
[x] 5.6.9

Current behavior:
If routes are defined with optional segment values then the ionic back button can result in errors.

Use case: A page that has optional parameters and handles more than one route.

Because there is no way to specify optional segments in routes the page gets mapped to multiple url values. For example:

<ion-router useHash>
    <ion-route url="/:s1/:s2" component="my-page" />
    <ion-route url="/:s1" component="my-page" />
    <ion-route url="" component="my-page" />
</ion-router>

Here is the sequence of steps:

  1. Navigate to /a. The component renders with props {s1: 'a', s2: undefined} as expected.
  2. Push /a/b onto the navigation stack. Another instance of the page renders with {s1: 'a', s2:'b'} as expected.
  3. Click on <ion-back-button> on the page. The prior page is displayed correctly but the url does not get updated so it is now incorrect.

The following entry appears in the log:

 [ion-router] router could not match path because some required param is missing

This error occurs because it finds a different route than the one that matched when the page was displayed.

The following code is where the error occurs:

// ion-router.tsx
async navChanged(direction: RouterDirection): Promise<boolean> {
  if (this.busy) {
    console.warn('[ion-router] router is busy, navChanged was cancelled');
    return false;
  }
  const { ids, outlet } = await readNavState(window.document.body);
  const routes = readRoutes(this.el);
  const chain = routerIDsToChain(ids, routes);
  if (!chain) {
    console.warn('[ion-router] no matching URL for ', ids.map(i => i.id));
    return false;
  }
  const path = chainToPath(chain);
  if (!path) {
    console.warn('[ion-router] router could not match path because some required param is missing');
    return false;
  }
  this.setPath(path, direction);
  await this.safeWriteNavState(outlet, chain, ROUTER_INTENT_NONE, path, null, ids.length);
  return true;
}

When the back button executes, navChanged() is called and the following happens:

  • The calculated value of chain is the route with all of the segments (<ion-route url="/:s1/:s2" component="my-page" />) vs. the route that matched for the first page that was displayed.
  • The call to chainToPath() fails because the page being displayed only specifies a value for s1 and it thinks s2 should be required.
  • The method exits without setting the path or calling safeWriteNavState.

It looks like the method is assuming that it can cancel the navigation because it thinks there is no match but in reality <ion-nav> has already updated the displayed page.

Expected behavior:

  • The back button should result in the correct url that matches the displayed content
  • Other failure conditions (e.g. busy) should also ensure that the state is consistent

One other issue that could occur:

  • Because of the lag in updating the url (based on animation delays?) it is possible for a page to render based on a url that is different from the original route it matched so there can be tricky timing issues if an app attempts to parse information from the current url.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions