Angular: 如何在不改变路由的情况下更新queryParams

260

我试图更新(添加,删除)组件的查询参数。在AngularJS中,这是可能的,感谢:

$location.search('f', 'filters[]'); // setter
$location.search()['filters[]'];    // getter

我有一个应用程序,其中用户可以筛选、排序等操作列表。我希望在URL的查询参数中设置所有已激活的过滤器,以便用户可以复制/粘贴URL或与其他人共享。

然而,我不想在选择任何筛选器时重新加载页面

使用新路由器能否实现这一点?

13个回答

514
你可以使用新的查询参数导航到当前路由,这样不会重新加载页面,但会更新查询参数。
在组件中可以这样做:
import { ActivatedRoute, Router } from '@angular/router';

constructor(
  private router: Router,
  private activatedRoute: ActivatedRoute,
) { }

public myMethodChangingQueryParams() {
  const queryParams: Params = { myParam: 'myNewValue' };

  this.router.navigate(
    [], 
    {
      relativeTo: this.activatedRoute,
      queryParams, 
      queryParamsHandling: 'merge', // remove to replace all query params by provided
    }
  );
}

请注意,虽然它不会重新加载页面,但会向浏览器的历史记录中添加一个新条目。如果您想要替换历史记录中的条目而不是添加新值,您可以使用{ queryParams: queryParams, replaceUrl: true }
编辑: 正如评论中已经指出的那样,我的原始示例中缺少了[]relativeTo属性,因此它可能会改变路由,而不仅仅是查询参数。在这种情况下,正确的this.router.navigate用法如下:
this.router.navigate(
  [], 
  {
    relativeTo: this.activatedRoute,
    queryParams: { myParam: 'myNewValue' },
    queryParamsHandling: 'merge'
  }
);

将新参数值设置为null将从URL中删除该参数。

43
为了使程序正常运行,我不得不使用 [] 而不是 ['.'] - Jaime Gómez
5
需要使用queryParams['relativeTo'] = this.activatedRoute; 来使导航相对于当前页面。 - klonq
5
解决了原帖问题的良好解决方案,除非我漏掉了什么,否则它不能让你能够在当前页面的历史记录中前进和后退(例如更改筛选器或排序方式5次,每个URL都有自己的订单但相同的组件),并且页面内容将会像您直接访问这5个URL时一样显示。 我认为您可以通过 this.activatedRoute.queryParams.subscribe 手动完成此操作,并在组件中进行各种更新,但是是否有一种简单的Angular方法只需加载路由即可自动处理前进和后退? - jmq
12
这实际上会破坏组件并在URL参数更改时每次调用ngOnit()。有没有办法避免调用ngOnit()? - undefined
2
使用router.navigate时,它会滚动到窗口的顶部,有什么想法可以禁用它吗? - Hese
显示剩余11条评论

53

@Radosław Roszkowiak的答案几乎正确,只是需要加入relativeTo: this.route,如下所示:

constructor(
    private router: Router,
    private route: ActivatedRoute,
) {}

changeQuery() {
    this.router.navigate(['.'], { relativeTo: this.route, queryParams: { ... }});
}

27

在Angular 5中,您可以通过解析当前的URL轻松获取并修改urlTree的副本。这将包括查询参数和片段。

  let urlTree = this.router.parseUrl(this.router.url);
  urlTree.queryParams['newParamKey'] = 'newValue';

  this.router.navigateByUrl(urlTree); 

“正确的方法”修改查询参数可能是使用createUrlTree,如下所示创建一个新的UrlTree,同时让我们使用NavigationExtras进行修改。

import { Router } from '@angular/router';

constructor(private router: Router) { }

appendAQueryParam() {

  const urlTree = this.router.createUrlTree([], {
    queryParams: { newParamKey: 'newValue' },
    queryParamsHandling: "merge",
    preserveFragment: true });

  this.router.navigateByUrl(urlTree); 
}
为了移除一个查询参数,你可以将它设置为undefinednull

2
navigateByUrlnavigate不同 - 不仅仅是UrlTree参数。请参见https://dev59.com/fFcO5IYBdhLWcg3w-VxX#45025432。 - Drenai

22

得票最高的答案对我有所帮助。浏览器URL保持不变,但我的 routerLinkActive 在导航后不再起作用。

我的解决方案是使用 router.navigate().then(() => location.go()):

import { Component } from "@angular/core";
import { Location } from "@angular/common";
import { HttpParams } from "@angular/common/http";

export class whateverComponent {
  constructor(private readonly location: Location, private readonly router: Router) {}

  addQueryString() {
    const params = new HttpParams();
    params.append("param1", "value1");
    params.append("param2", "value2");
    this.location.go(this.router.url.split("?")[0], params.toString());
  }
}

我使用HttpParams来构建查询字符串,因为我已经在使用httpClient发送信息了。但你也可以自己构建它。

this._router.url.split("?")[0]是为了从当前url中删除所有先前的查询字符串。


10
这应该被接受为正确答案,router.navigate也会刷新ngOnInit,而location.go则不会!这很重要,因为如果你的组件执行了一些ngOnInit逻辑(如HTTP调用等),当你使用router.navigate时它们将再次被调用。你不希望发生这种情况。 - Danny Hoeve
为什么你需要路由器,仅仅是为了url吗?难道不觉得this.location.path().split...更好吗? - Vladimir
@Vladimir,你可能是对的,我在我的组件中还用它做了其他用途,所以它不仅仅是为那个而存在的。不过我现在没有时间去验证。 - moi_meme
如果您想保留查询参数,例如促销或Google跟踪参数,则此方法将无效。 - Jon Tinsman
如果有人想要在不将 URL 推送到历史记录的情况下更改它,请使用 location.replaceState - Michał Stochmal
1
那是唯一对我有用的答案,而且实际上很有道理。 - spinner

15

尝试

this.router.navigate([], { 
  queryParams: {
    query: value
  }
});

除了单引号之外,将适用于相同的路由导航。


11

如果你想在不改变路由的情况下更改查询参数,请参考下面的示例: 例如: 当前路由为:/search 目标路由是(在不重新加载页面的情况下):/search?query=love

    submit(value: string) {
      this.router.navigate( ['.'],  { queryParams: { query: value } })
        .then(_ => this.search(q));
    }
    search(keyword:any) { 
    //do some activity using }

注意:您可以使用this.router.navigate(['search']而不是this.router.navigate(['.'])


我不确定使用 [.] 是否比 relativeTo: 更好,但 .then() 很有帮助 - 我不知道 navigate() 返回一个 promise,所以很高兴你发了这个帖子! - Drenai
2
我们可以简单地使用[],而不是使用['.']或['search']。 - Sahil Bhatia

8
我最终将urlTreelocation.go结合起来。
const urlTree = this.router.createUrlTree([], {
       relativeTo: this.route,
       queryParams: {
           newParam: myNewParam,
       },
       queryParamsHandling: 'merge',
    });

    this.location.go(urlTree.toString());

不确定 toString 是否会引起问题,但不幸的是 location.go 似乎基于字符串。


这不会触发查询参数映射可观察对象的观察者更新。 - dude

7
更好的做法 - 直接使用 HTML:

<a [routerLink]="[]" [queryParams]="{key: 'value'}">您的查询参数链接</a>

请注意,这里使用了空数组而不是仅使用 routerLink=""[routerLink]="''"

1
为什么是一个空数组?[routerLink] =“”有效。 - ScubaSteve
这似乎在 Angular 11 或更高版本中不再起作用了。 - Jonathan002

6
Angular 的 Location 服务应该用于与浏览器的 URL 交互,而不是用于路由。这就是为什么我们要使用 Location 服务。
Angular 的 HttpParams 用于创建查询参数。请记住,HttpParams 是不可变的,这意味着在创建值时必须链接它们。
最后,使用 this._location.replaceState 来更改 URL 而不重新加载页面/路由,使用原生 js location.path 获取没有参数的 URL 来每次重置参数。
constructor(
    private _location: Location,
) {}

...

updateURLWithNewParamsWithoutReloading() {
    const params = new HttpParams().appendAll({
        price: 100,
        product: 'bag'
    });

    this._location.replaceState(
        location.pathname,
        params.toString()
    );
}

这是我的正确答案,因为我不想触发一个新的页面重新加载。 - PhilWilliammee

2

首先,我们需要从Angular Router中导入路由模块并声明其别名

import { Router } from '@angular/router'; ---> import
class AbcComponent implements OnInit(){
constructor(
    private router: Router ---> decalre alias name
  ) { }
}

1. 你可以使用“router.navigate”函数并传递查询参数来更改查询参数。

this.router.navigate([], { queryParams: {_id: "abc", day: "1", name: "dfd"} 
});

它将更新当前即激活路由中的查询参数

  1. 以下代码将通过查询参数_id、day和name重定向到abc页面

    this.router.navigate(['/abc'], { queryParams: {_id: "abc", day: "1", name: "dfd"} });

    它将在"abc"路由中更新三个查询参数的查询参数

获取查询参数的方法如下:

    import { ActivatedRoute } from '@angular/router'; //import activated routed

    export class ABC implements OnInit {

    constructor(
        private route: ActivatedRoute //declare its alias name
      ) {}

    ngOnInit(){
       console.log(this.route.snapshot.queryParamMap.get('_id')); //this will fetch the query params
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接