8

In Angular2, is there a way to update a matrix parameter, but navigate to the same component? Basically would like to transition easily from a url that looks like:

/search;term=paris;pageSize=24;currentPage=1;someFilter=value;

to the same thing, but with currentPage updated upon pagination:

/search;term=paris;pageSize=24;currentPage=2;someFilter=value;

using router.navigate hasn't appeared to be the best option, since I'd have to write plenty of my own logic to re-construct the url for navigate's use (re-constructing something that already exists).

For kicks and giggles, I did try

this._router.navigate([this._router.url, {currentPage: this.pagination.currentPage}]);

but (as you might expect), the router is dumb to the fact that there are matrix parameters on the current url, so the result is not pretty.

EDIT: Should also mention that there may be any number of additional key/value pairs of matrix parameters, so hard-coding any route is out of the question

EDIT: I've since tried to use preserveQueryParams: true like:

this._router.navigate(["..", {currentPage: this.pagination.currentPage}], {preserveQueryParams: true});

in order to get to an easily usable/transferable solution, but that did not keep the matrix params on the route.

UPDATE: I've since implemented my own logic to get the required behavior, so here's a code snippet for the solution:

  let currentParamsArr: Params = this.route.snapshot.params;
  let currentParamsObj: any = { };
  for (let param in currentParamsArr) {
    currentParamsObj[param] = currentParamsArr[param];
  }
  currentParamsObj.currentPage = +pageNum; //typecasting shortcut

  this._router.navigate([currentParamsObj], { relativeTo: this.route });

This code loops through the parameters (as they stand in the snapshot), and creates an object out of them, then adds/edits the parameter that I desire to update, then navigates to the "new" route

But, this is not pretty, as I'll essentially have this same logic in many places of the program or have to make a modification to Router or provide some other generic method.

CozyAzure
  • 8,280
  • 7
  • 34
  • 52
Corbfon
  • 3,514
  • 1
  • 13
  • 24
  • Perhaps `this._router.navigate([this._router.url, {currentPage: this.pagination.currentPage}], {preserveQueryParams: true});` – Günter Zöchbauer Dec 18 '16 at 12:09
  • sounds like the right path! Just gave it a shot, and no dice. I think the issue has something to do with this._router.url being a string (it still encoded the semicolons). Is there a more complex object that the navigate method could be used with, which would still contain the query params explicitly? – Corbfon Dec 18 '16 at 15:45
  • This gets me _almost_ where I want: `this._router.navigate(["..", {currentPage: this.pagination.currentPage}]);`, except that it tosses the other query parameters. Also tried adding `{preserveQueryParams: true}` here and it didn't alter behavior – Corbfon Dec 18 '16 at 15:50
  • 1
    I guess you need to build a new urlTree using `this._router.createUrlTree()` where you pass the parts that build the route by getting them from `ActivatedRoute` or `this._router..routerState` and traverse the route tree to get all parts. I don't know of an easier way. – Günter Zöchbauer Dec 18 '16 at 15:52
  • Ah, something I forgot to mention (and perhaps why `preserveQueryParams` didn't work: I'm using matrix parameters and not query parameters in this instance – Corbfon Dec 18 '16 at 18:59
  • `preserveQueryParams` shozld work for matrix params as well. The difference is that query params are query params added to the root segment, while matrix params are query params added to child route segment. – Günter Zöchbauer Dec 18 '16 at 19:02
  • 1
    @GünterZöchbauer you were right about making a urlTree. I think it's probably the cleanest approach. I've posted the implementation in an answer – Corbfon Jan 25 '17 at 17:43

3 Answers3

5

The approach that ended up working well for me is this:

let route: ActivatedRoute;
const newUrl = router.createUrlTree([
  merge({'a': 123}, route.snapshot.params)
], {relativeTo: route});

By using merge, you can add, update and subtract url parameters and then use router.navigateByUrl(newUrl) in order to execute.

add: merge({newParam: 111}, route.snapshot.params)

update: merge({param: 111}, route.snapshot.params)

subtract: merge({param: null}, route.snapshot.params)

Hope others find this as useful as I have.

Another example using Object.assign instead of merge:

let route = this.route; // The imported ActivatedRoute
let currentParamsObj: Params = Object.assign({}, route.snapshot.params);

let newParam = {};
newParam[key] = value;

let newUrl = this._router.createUrlTree([
        Object.assign(currentParamsObj, newParam)
    ], {relativeTo: this.route });

this._router.navigateByUrl(newUrl);
muttonUp
  • 6,351
  • 2
  • 42
  • 54
Corbfon
  • 3,514
  • 1
  • 13
  • 24
0

Have you tried using something like

this._router.navigateByUrl('/search;term='+this.term+';pageSize='+this.pageSize+';currentPage='+this.currentPage+';someFilter='+this.someFilter;
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Jonathan Niu
  • 311
  • 1
  • 8
  • I don't think this will work. Matrix parameters are query parameters added to child segements. – Günter Zöchbauer Jan 24 '17 at 09:43
  • @GünterZöchbauer not sure what you mean by 'query parameters added to child segments'. I'm currently using matrix params for optional params and a similar approach works fine for me. – Jonathan Niu Jan 24 '17 at 10:11
  • queryParams are added using `NavigationExtras` https://angular.io/docs/ts/latest/api/router/index/NavigationExtras-interface.html, https://angular.io/docs/ts/latest/api/router/index/Router-class.html#!#navigateByUrl-anchor – Günter Zöchbauer Jan 24 '17 at 10:13
  • @GünterZöchbauer Thanks for the clarification, but I still don't see why this approach would not work since all params would be applied to the /search route in the same format as he originally provided – Jonathan Niu Jan 24 '17 at 10:15
  • I'll have a closer look when I find some time to check if it actually works. – Günter Zöchbauer Jan 24 '17 at 10:21
  • @JonathanNiu see the answer I just posted. It ends up being really clean, and is easy to make flexible with a function – Corbfon Jan 25 '17 at 13:23
0

In my case, I had a parent and child router, with params on the parent I needed to modify: /my-parent;x=1;y=2/my-child

Working off of @Corbfon's solution, I was able to modify the parent route like this:

    // modify the context params in the PARENT route
    const newParam = {'z': '3'};
    const parentParams: Params = Object.assign({ ...this.activatedRoute.parent.snapshot.params }, newParam);

    // note: this approach doesn't preserve params on the child route (but there aren't any in my case)
    const currentChildPath = this.activatedRoute.routeConfig.path;

    // notice it's relativeTo the parent route
    const newUrl = this.router.createUrlTree([parentParams, currentChildPath],
      {relativeTo: this.activatedRoute.parent});
    this.router.navigateByUrl(newUrl);

Hope this helps someone else.

P.S. Don't forget to use .snapshot.params instead of .params on the ActivatedRoute.

RMorrisey
  • 7,637
  • 9
  • 53
  • 71