Ionic 4 optional parameter in route


#1

Is it possible to add an optional parameter to a route in Ionic 4?

{ path: 'item/:item_id/:other_id', loadChildren: './pages/item/item.module#ItemPageModule' }

this.router.navigateByUrl('/item/' + '222/999'); this works

this.router.navigateByUrl('/item/' + '222'); this does not


#2

One option is to not include the optional parameter in the router and pass it like this:
this.router.navigateByUrl('/item/' + '222?n=999');

Where the router looks like:
{ path: 'item/:item_id', loadChildren: './pages/item/item.module#ItemPageModule' }

And in the ItemPage, grab the param like so:
let n_id = this.platform.getQueryParam("n");

In my case, I use the router param (222) to pass an item_id and I use the n=999 param as an optional in case I want to reference it in another service to pass unlimited vars to the loading page.
This way, direct links to …item/222 will still work.

If there are other methods…


#3

Maybe https://stackoverflow.com/a/36320864/5404186 ?


#4

See documentation here https://angular.io/guide/router#heroes-list-optionally-selecting-a-hero

But I think something like this should work

this.router.navigate(['/item', { ids: [222,999] }]);

then in the component

    this.theIds$ = this.route.paramMap.pipe(
      switchMap((params: ParamMap) => {
        return params.get('ids');
      })
    );

#5

Thanks @reedrichards and @aaronksaunders I have not tried these yet but will either enable a direct link to work?

As in…:
mysite.com/item/222/999

It is not the navigating within the app I am concerned with. It is that when I try to catch an external link, like above, I get this route is not found.

@reedrichards I do not see how the link you pointed to differs from my example above:
{path: '/component/:id/:id2',name: 'MyCompB', component:MyCompB}
vs
{ path: 'item/:item_id', loadChildren: './pages/item/item.module#ItemPageModule' }

@aaronksaunders how would a static link look with that example?


#6

About the link, I just pointed it because the way the route is called, with an object and array like

this._router.navigate( ['MyCompB', {id: "someId", id2: "another ID"}]);

but I don’t know if it applies, that’s why I wrote “maybe” :wink:

p.s.:

since you are trying to get an external url, if the params are the problems, maybe you could declare the route without params in app-routing and try to get the params like this.platform.getQueryParam('myParam'); in app.component.ts?


#7

Ya, in my second post I am essentially doing that. Droping the second param in the route and grabbing via
let n_id = this.platform.getQueryParam("n");
Which does work fine but was wondering if there was a way within app-routing.

I was not clear enough in my first post in that the method needs to also work with static links.


#8

Sorry it was my misunderstood, I don’t know that about the routing

I’ve got an external link with two params which leads to a route, I grab both params in app.component and put them in a provider. Actually I went the easy way for all my routes, they are all are defined without params in app-routing and I use providers. It made my migration easier

I hope @aaronksaunders or someone else will be able to solve your issue


#9

Its OK, I have a working solution using getQueryParam.

My approach I am trying right now is using a navigation service to catch all internal routing with a single id.
I have many sections in my app all with groups with like: ItemsPage, ItemPage, ItemEditorPage, ItemDeailsPage
Whenever a call to ItemPage is used, it first goes to this NavigationService along with any amount of Object data and stored in an array. The objectID in the array is passed to the ItemPage ( using the first app-route param ) and then all the data I want is passed through from the NavigationService’s array to the ItemPage.
I think that is what you are doing as well but this way I can use this one service for all my other services’ routing needs.
I really miss being able to pass unlimited data in the v3 NavController. This is my attempt.
It also gives me a record of all the internal routes used - for history seeking and user interaction monitoring.

If I do not like how this goes, I will go back to using passing large object data on a per service basis. As in the the example above, my ItemsService ( one of many ) would handle megadata only for Items.
( got off topic I think )


#10

Kind of yep, but for me it’s ok I solve everything with one provider too, like

export interface ChatNavParams {
   paramA: string,
   paramB: boolean;
   paramC? CouldBeAnotherInterfaceOrClassAndOptional;
}
 
export interface OtherEtc {
}

@Injectable({
   providedIn: 'root'
})
export class NavParamsService {
 
     navOtherEtc: OtherEtc;
    
     chatNavParams: ChatNavParams;
}

which I use in my pages, like

 async navigate() {
   this.navParamsService.chatNavParams = {
      paramA: 'whatever',
      paramB: true;
      paramC: this.myObject;
   }
  
  await this.navCtrl.goForward('/somewhere');
}

and then I guess in the other page for example

 ionViewDidEnter() {
   this.value = this.navParamsService.chatNavParams.paramA;
   this.active = this.navParamsService.chatNavParams.paramB;
   this.myObject = this.navParamsService.chatNavParams.paramC;
 }

which is really close to the “old” navigation and navparams feature


#11

Great, I’ll have a closer look later - need to get some outside time in - cheers.


#12

In your example, what happens when you go back a page or pages? Are the params not then lost?

I am not committed yet to any particular way to achieve this but this is what I am working with.

I have a NavigationService with a couple of arrays that I save to storage, one being session_routes.

I have a function I call from other pages when I go to navigate around the app.

async addRoute(routeObj) {
    this.session_routes.push(routeObj);
    this.storage.set('session_routes', JSON.stringify(this.session_routes));
    await this.navCtrl.goForward(routeObj.url);
  }

I call this like so:
this.NavigationService.addRoute({url: '/item/' + '1094' +'?n='+this.NavigationService.session_routes.length, params: {anything: "somecrazydata"}});

In the receiving page I have:

if (this.platform.getQueryParam("n")) {
   this.NavigationService.session_routes[this.platform.getQueryParam("n")];
}

The URL param ‘n’ is what I wanted to get rid of in this thread. I wanted it to be an option in the app-route params, so that static links did not need to pass it. I also do not like the look of it as this is mainly a PWA.

I welcome thoughts on this approach.

My other approach I am considering is saving extra param data in the ItemService itself.
In ItemService, I have all the Items in an array. As each is loaded in ItemPage ( via navCtrl or url ), any extra param data is saved to the Item object in the array. So, whenever it is reloaded via GoBack or direct link, the last loaded param data is still there.
This will eliminate the need for using the URLparam ‘n=’ ( or similar ) but now I am dealing with this across multiple Services.
@reedrichards this would be like your example but for multiple Services. Not ideal.

So, it looks like now I am going to try and combine the two… using the above NavigationService to catch navigations and then push the extra data to the Item object in the Items array in the ItemsService.

And it looks like this:

NavigationService:

async addRoute(routeObj) {
    this.session_routes.push(routeObj);
    this.storage.set('session_routes', JSON.stringify(this.session_routes));
    let item_id = routeObj.params.item_id
    let item = this.ItemsService.items_ar.filter(item => item.item_id == item_id)[0];
    if(item){
      item.navParams = routeObj.params;
    }
    await this.navCtrl.goForward(routeObj.url);
  }

ItemPage:

this.item_id = this.route.snapshot.paramMap.get('item_id');
this.item = this.ItemsService.items_ar.filter(item => item.item_id == this.item_id)[0];
console.log("MY ITEM NAVPARAMS: "+JSON.stringify(this.item.navParams));

Sending Page:
this.NavigationService.addRoute({go: true, url: '/item/' + '1094', params: {item_id: "1094", nav_id: this.NavigationService.session_routes.length, extra: "some crazy data"}});

This could use refinement but it accomplishes multiple things:

  • Sends extra paramData
  • Solves optional app-route param problem
  • Eliminates extra url param ‘n=’ etc…
  • Works for static links
  • Saves historical paramData

Overkill - likely.


#13

Of course not :wink: Since I put the param in a provider, as long as I don’t update or delete them or as long as I don’t stop the app, they stay in memory

Nice solution you’ve got there, seems your data are more complicated than me. In my case I don’t have to iterate thru an array, I get the object(s) and I just retrieve them, therefor I don’t have to combine the two solutions like you do

But really nice tricks :wink:


#14

Thanks, but about the lost data…
If you go to ItemPage with an item_id of 222 ( /item/222 ) you have specific data for item 222.
If you then go to /item/999 where is the item 22 data saved?

So when you are going through lists of items and you want to go back… you would need to set the provider paramData again.

Among my other desires, I am trying to preserve the data as the user creates it ( by their choice of user interaction ) and recall it while navigating through history.

I get that it really depends on what one wants to achieve. I am just looking for significant problems or better yet, built in solutions for this.

I appreciate all the feedback you have given on this.


#15

How do you solve the problem in v3?

Just take the same approach. Really for me I just added a provider, defined an interface and an object in it and then replaced the code in my pages et voilà

But like I said your data structure is way more complicated than mine again


#16

this is why there are state management libraries that handle alot of this for you… using a provider works but it gets messy as your application grows in complexity

  constructor(
    private route: ActivatedRoute,
    private router: Router,
  ) {
    console.log(this.route.snapshot.paramMap.get('ids'))
  }

With this URL

https://angular-rptcu8.stackblitz.io/detail/10;ids=[21,22,23]

You get this result

[21,22,23]


#17

Another alternative is adding multiple paths.

 { path: 'item', loadChildren: './pages/items/items.module#ItemsPageModule' },
  { path: 'item/:item_id', loadChildren: './pages/items/items.module#ItemsPageModule' },
  { path: 'item/:item_id/:optional_id', loadChildren: './pages/items/items.module#ItemsPageModule' },

The above will load all of these:

this.router.navigateByUrl('/item');
this.router.navigateByUrl('/item/' + '222');
this.router.navigateByUrl('/item/' + '222/999');

The order is important and so is leaving the trailing slash on the first path.