更改路线参数而无需以角度2重新加载

时间:2016-02-25 04:37:27

标签: javascript angular routes angular2-routing

我正在使用角度2,谷歌地图等制作房地产网站,当用户更改地图的中心时,我会执行搜索到api,指示地图的当前位置以及半径。问题是,我想在不重新加载整个页面的情况下在URL中反映这些值。那可能吗?我找到了一些使用AngularJS 1.x的解决方案,但没有关于angular 2的解决方案。

10 个答案:

答案 0 :(得分:59)

你可以使用location.go(url)基本上改变你的网址,而不改变申请路线。

  

注意这可能会导致其他影响,例如从当前路线重定向到子路线。

描述location.go

Related question不会与Router发生变化。

答案 1 :(得分:41)

使用location.go(url)是可行的方法,但不要对网址进行硬编码,而应考虑使用router.createUrlTree()生成网址。

鉴于您要执行以下路由器调用:this.router.navigate([{param: 1}], {relativeTo: this.activatedRoute})但无需重新加载组件,可将其重写为:

const url = this
        .router
        .createUrlTree([{param: 1}], {relativeTo: this.activatedRoute})
        .toString();

 this.location.go(url);

答案 2 :(得分:7)

我在使用angular2的RCx版本时遇到了很大的麻烦。 Location包已移动,并且在constructor()内运行location.go()不起作用。它需要在生命周期中为ngOnInit()或更高版本。以下是一些示例代码:

import {OnInit} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  selector: 'example-component',
  templateUrl: 'xxx.html'
})
export class ExampleComponent implements OnInit
{
  constructor( private location: Location )
  {}

  ngOnInit()
  {    
    this.location.go( '/example;example_param=917' );
  }
}

以下是此事的角度资源: https://angular.io/docs/ts/latest/api/common/index/Location-class.html https://angular.io/docs/ts/latest/api/common/index/LocationStrategy-class.html

答案 3 :(得分:7)

对于像我这样的人发现这个问题,以下内容可能会有用。

我遇到了类似的问题,最初尝试使用location.go和location.replaceState,如其他答案所示。但是当我不得不导航到应用程序上的另一个页面时,我遇到了问题,因为导航是相对于当前路线而当前路线没有被location.go或location.replaceState更新(路由器什么都不知道关于这些对URL做了什么)

本质上我需要一个解决方案,当路由参数改变时DID不会重新加载页面/组件,但DID会在内部更新路由状态。

我最终使用了查询参数。您可以在此处找到更多相关信息:https://angular-2-training-book.rangle.io/handout/routing/query_params.html

因此,如果您需要执行诸如保存订单和获取订单ID之类的操作,则可以更新您的网页网址,如下所示。更新地图上的中心位置和相关数据将类似

// let's say we're saving an order. Initally the URL is just blah/orders
save(orderId) {
    // [Here we would call back-end to save the order in the database]

    this.router.navigate(['orders'], { queryParams: { id: orderId } });
    // now the URL is blah/orders?id:1234. We don't reload the orders
    // page or component so get desired behaviour of not seeing any 
    // flickers or resetting the page.
}

并在ngOnInit方法中跟踪它,如:

ngOnInit() {
    this.orderId = this.route
        .queryParamMap
        .map(params => params.get('id') || null);
    // orderID is up-to-date with what is saved in database now, or if
    // nothing is saved and hence no id query paramter the orderId variable
    // is simply null.
    // [You can load the order here from its ID if this suits your design]
}

如果您需要使用新的(未保存的)订单直接进入订单页面,您可以执行以下操作:

this.router.navigate(['orders']);

或者,如果您需要直接进入现有(已保存)订单的订单页面,您可以执行以下操作:

this.router.navigate(['orders'], { queryParams: { id: '1234' } });

答案 4 :(得分:3)

我用这种方式得到它:

const queryParamsObj = {foo: 1, bar: 2, andThis: 'text'};

this.location.replaceState(
  this.router.createUrlTree(
    [this.locationStrategy.path().split('?')[0]], // Get uri
    {queryParams: queryParamsObj} // Pass all parameters inside queryParamsObj
  ).toString()
);

-编辑-

我认为我应该为此添加更多信息。

如果您使用this.location.replaceState()应用程序的路由器,则不会更新,因此,如果以后使用路由器信息,则在浏览器中并不相同。例如,如果您使用localizeService更改语言,则在将语言切换后,您的应用程序将返回到使用this.location.replaceState()进行更改之前的最后一个URL。

如果您不希望出现这种情况,则可以选择其他更新URL的方法,例如:

this.router.navigate(
  [this.locationStrategy.path().split('?')[0]],
  {queryParams: queryParamsObj}
);

在此选项中,您的浏览器也不会刷新,但是您的URL更改也会注入到应用程序的Router中,因此,当您切换语言时,不会像{{1 }}。

当然,您可以根据需要选择方法。首先,它更轻便,因为您只需要在浏览器中更改this.location.replaceState()即可使用应用程序。

答案 5 :(得分:2)

对我而言,它实际上是Angular 4.4.5的混合。

使用router.navigate通过不尊重realtiveTo:activatedRoute部分来破坏我的网址。

我最终得到了:

this._location.go(this._router.createUrlTree([this._router.url], { queryParams: { profile: value.id } }).toString())

答案 6 :(得分:1)

在更改网址时使用属性queryParamsHandling:“合并”。

this.router.navigate([], {
        queryParams: this.queryParams,
        queryParamsHandling: 'merge',
        replaceUrl: true,
});

答案 7 :(得分:0)

就我而言,我需要删除网址的查询参数,以防止用户看到它。

我发现replaceState比location.go安全,因为带有旧查询参数的路径已从堆栈中消失,用户可以重做与此查询相关的查询。因此,我更喜欢这样做:

this.location.replaceState(this.router.url.split('?')[0]);

点击location.go,返回浏览器将返回带有查询参数的旧路径,并将其保留在导航堆栈中。

this.location.go(this.router.url.split('?')[0]);

答案 8 :(得分:0)

我有与问题中所述类似的要求,并且花了一些时间根据现有答案来解决问题,所以我想分享我的最终解决方案。

要求

用户可以更改我的视图状态(从技术上来说,是组件的状态)(过滤器设置,排序选项等)。当状态发生变化时,即用户更改了排序方向,我想:

  • 在URL中反映状态更改
  • 处理状态更改,即进行API调用以接收新的结果集

此外,我想:

  • 根据情况指定是否在浏览器历史记录(后退/前进)中考虑URL更改
  • 使用复杂的对象作为状态参数,以提供更大的灵活性来处理状态更改(可选,但使生活更轻松,例如,当某些状态更改触发后端/ API调用而其他状态由前端内部处理时)

解决方案:更改状态而无需重新加载组件

使用路由参数或查询参数时,状态更改不会导致组件重新加载。组件实例保持活动状态。我认为没有充分的理由通过使用Location.go()location.replaceState()来弄乱路由器的状态。

var state = { q: 'foo', sort: 'bar' }; 
var url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams: state }).toString();
this.router.navigateByUrl(url);

state对象将由Angular的Router转换为URL查询参数:

https://localhost/some/route?q=foo&sort=bar

解决方案:处理状态更改以进行API调用

可以通过订阅ActivatedRoute.queryParams来处理上面触发的状态更改:

export class MyComponent implements OnInit {

   constructor(private activatedRoute: ActivatedRoute) { }

   ngOnInit()
   {
      this.activatedRoute.queryParams.subscribe((params) => {
         // params is the state object passed to the router on navigation 
         // Make API calls here
      });
   }

}

以上示例的state对象将作为params的{​​{1}}自变量传递。如有必要,可以在处理程序中进行API调用。

但是:我希望直接在组件中处理状态更改,并避免绕过queryParams。 IMO导航路由器,让Angular进行路由魔术并处理ActivatedRoute.queryParams更改以执行某些操作,这完全模糊了我组件中发生的与可维护性和代码可读性有关的事情。我该怎么做:

将传递给queryParams的状态与组件中的当前状态进行比较,如果在那里没有更改,则不执行任何操作,而是直接处理状态更改:

queryParams

解决方案:指定浏览器历史记录行为

export class MyComponent implements OnInit {

   private _currentState;

   constructor(private activatedRoute: ActivatedRoute) { }

   ngOnInit()
   {
      this.activatedRoute.queryParams.subscribe((params) => {
         // Following comparison assumes, that property order doesn't change
         if (JSON.stringify(this._currentState) == JSON.stringify(params)) return;
         // The followig code will be executed only when the state changes externally, i.e. through navigating to a URL with params by the user
         this._currentState = params;
         this.makeApiCalls();
      });
   }

   updateView()
   {          
      this.makeApiCalls();
      this.updateUri();
   }    

   updateUri()
   {
      var url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams: this._currentState }).toString();
this.router.navigateByUrl(url);
   }
}

解决方案:将复杂对象作为状态

这超出了最初的问题,但可以解决常见的情况,因此可能有用:上面的var createHistoryEntry = true // or false var url = ... // see above this.router.navigateByUrl(url, { replaceUrl : !createHistoryEntry}); 对象仅限于平面对象(该对象仅具有简单的string / bool / int / ...属性,但没有嵌套对象)。我发现了这种限制,因为我需要区分需要通过后端调用处理的属性和仅由组件内部使用的其他属性。我想要一个像这样的状态对象:

state

要将此状态用作路由器的queryParams对象,需要对其进行展平。我只是var state = { filter: { something: '', foo: 'bar' }, viewSettings: { ... } }; 对象的所有第一级属性:

JSON.stringify

然后返回,当处理由路由器传入的queryParams返回时:

private convertToParamsData(data) {
    var params = {};

    for (var prop in data) {
      if (Object.prototype.hasOwnProperty.call(data, prop)) {
        var value = data[prop];
        if (value == null || value == undefined) continue;
        params[prop] = JSON.stringify(value, (k, v) => {
          if (v !== null) return v
        });
      }
    }
    return params;
 }

最后:现成的Angular服务

最后,所有这些都隔离在一个简单的服务中:

private convertFromParamsData(params) {
    var data = {};

    for (var prop in params) {
      if (Object.prototype.hasOwnProperty.call(params, prop)) {
        data[prop] = JSON.parse(params[prop]);
      }
    }
    return data;
}

可以这样使用:

import { Injectable } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { Location } from '@angular/common';
import { map, filter, tap } from 'rxjs/operators';

@Injectable()
export class QueryParamsService {

  private currentParams: any;

  externalStateChange: Observable<any>;

  constructor(private activatedRoute: ActivatedRoute, private router: Router, private location: Location) {

    this.externalStateChange = this.activatedRoute.queryParams
      .pipe(map((flatParams) => {
        var params = this.convertFromParamsData(flatParams);
        return params
      }))
      .pipe(filter((params) => {
        return !this.equalsCurrentParams(params);
      }))
      .pipe(tap((params) => {
        this.currentParams = params;
      }));
  }

  setState(data: any, createHistoryEntry = false) {
    var flat = this.convertToParamsData(data);
    const url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams: flat }).toString();
    this.currentParams = data;
    this.router.navigateByUrl(url, { replaceUrl: !createHistoryEntry });
  }

  private equalsCurrentParams(data) {
    var isEqual = JSON.stringify(data) == JSON.stringify(this.currentParams);
    return isEqual;
  }

  private convertToParamsData(data) {
    var params = {};

    for (var prop in data) {
      if (Object.prototype.hasOwnProperty.call(data, prop)) {
        var value = data[prop];
        if (value == null || value == undefined) continue;
        params[prop] = JSON.stringify(value, (k, v) => {
          if (v !== null) return v
        });
      }
    }
    return params;
  }

  private convertFromParamsData(params) {
    var data = {};

    for (var prop in params) {
      if (Object.prototype.hasOwnProperty.call(params, prop)) {
        data[prop] = JSON.parse(params[prop]);
      }
    }
    return data;
  }
}

请不要忘记组件级别的@Component({ selector: "app-search", templateUrl: "./search.component.html", styleUrls: ["./search.component.scss"], providers: [QueryParamsService] }) export class ProjectSearchComponent implements OnInit { filter : any; viewSettings : any; constructor(private queryParamsService: QueryParamsService) { } ngOnInit(): void { this.queryParamsService.externalStateChange .pipe(debounce(() => interval(500))) // Debounce optional .subscribe(params => { // Set state from params, i.e. if (params.filter) this.filter = params.filter; if (params.viewSettings) this.viewSettings = params.viewSettings; // You might want to init this.filter, ... with default values here // If you want to write default values to URL, you can call setState here this.queryParamsService.setState(params, false); // false = no history entry this.initializeView(); //i.e. make API calls }); } updateView() { var data = { filter: this.filter, viewSettings: this.viewSettings }; this.queryParamsService.setState(data, true); // Do whatever to update your view } // ... } 语句为该组件创建一个新的服务实例。不要在应用程序模块上全局注册服务。

答案 9 :(得分:0)

在 2021 年,这是我使用的解决方案。使用 createUrlTree 创建 URL Tree 并使用 location

导航到路由
//Build URL Tree
    const urlTree = this.router.createUrlTree(["/employee/"+this.employeeId],{
      relativeTo: this.route,
      queryParams: params,
      queryParamsHandling: 'merge'
    });

//Update the URL 
this.location.go(urlTree.toString());
相关问题