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

Mar*_*ldo 91 javascript routes angular2-routing angular

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

小智 80

从RC6开始,您可以执行以下操作来更改URL而不更改状态,从而保留您的路线历史记录

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

import {Location} from '@angular/common'; 
// If you dont import this angular will import the wrong "Location"

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

  ngOnInit()
  {    
    this.location.replaceState("/some/newstate/");
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 结合@golfadas建议的网址创建,我们有一个胜利者! (2认同)

Pan*_*kar 71

您可以使用location.go(url)哪个基本上会更改您的网址,而不会改变应用程序的路线.

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

描述的相关问题location.go不会Router发生变化.

  • 如果有人想知道:从'angular2/platform/common'导入{Location}; (17认同)
  • `导入{Location}来自'@ angular/common';`在Angular 4中 (13认同)
  • 你有没有像 `constructor(private location: Location){ }` 这样的构造函数 (4认同)
  • @AdrianE,问题很可能是您输入了“location.go()”,而您应该输入“this.location.go()”。当你发出 `this.` 时,你调用了 Typescript 的位置接口。 (3认同)
  • 我的路由有一个名为'search'的参数,其中一个接收序列化版本的搜索字段,当列表状态第一次加载时我只使用this._routeParams.get('search')读取这些参数,反序列化过滤器并执行搜索.如果用户通过使用地图或搜索构面更改搜索字段,我只需使用路由器var instruction = this._router.generate(['Listing',{search:serializedFields}]的方法生成构造正确的url )然后使用this._location.go(instruction.urlPath)来更改url而不重新加载状态. (2认同)

gol*_*das 47

使用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);
Run Code Online (Sandbox Code Playgroud)

  • 这个答案解决了我的问题.一个问题,上面生成的url具有由";"(分号)分隔的参数.我们应该怎么做才能将查询中的每个参数与"&"分开? (7认同)
  • 这是 createUrlTree(commands: any[], navigationExtras?: NavigationExtras) 的声明。您必须使用 queryParams 位于 navigationExtras 而不是命令。createUrlTree([], {relativeTo: this.activatedRoute, queryParams: {param: 1} }) (2认同)

kri*_*_IV 8

我用这种方式来获取它:

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()
);
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

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

如果您使用this.location.replaceState()路由器的应用程序未更新,那么如果您稍后使用路由器信息,则在浏览器中不等于此信息。例如,如果您使用localizeService更改语言,则在将语言切换回您的应用程序后,您使用this.location.replaceState().

如果您不想要这种行为,您可以为更新 URL 选择不同的方法,例如:

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

在此选项中,您的浏览器也不会刷新,但您的URL更改也会注入到Router您的应用程序中,因此当您切换语言时,您不会遇到this.location.replaceState().

当然,您可以根据自己的需要选择方法。第一个更轻量,因为您不会更多地参与应用程序,而不是更改URL浏览器。


Luk*_*pin 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' );
  }
}
Run Code Online (Sandbox Code Playgroud)

以下是该问题的角度资源:https : //angular.io/docs/ts/latest/api/common/index/Location-class.html https://angular.io/docs/ts/latest/api/公共/索引/ LocationStrategy-class.html


Tor*_*Ali 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.
}
Run Code Online (Sandbox Code Playgroud)

并在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]
}
Run Code Online (Sandbox Code Playgroud)

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

this.router.navigate(['orders']);
Run Code Online (Sandbox Code Playgroud)

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

this.router.navigate(['orders'], { queryParams: { id: '1234' } });
Run Code Online (Sandbox Code Playgroud)


小智 5

更改 url 时使用属性 queryParamsHandling: 'merge'。

this.router.navigate([], {
        queryParams: this.queryParams,
        queryParamsHandling: 'merge',
        replaceUrl: true,
});
Run Code Online (Sandbox Code Playgroud)

  • 这会导致当前路由的组​​件被重新加载 (11认同)

Mar*_*arc 5

我有问题中描述的类似要求,根据现有答案花了一段时间才弄清楚,所以我想分享我的最终解决方案。

要求

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

  • 反映 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);
Run Code Online (Sandbox Code Playgroud)

state对象将被 Angular 转换为 URL 查询参数Router

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
      });
   }

}
Run Code Online (Sandbox Code Playgroud)

state上述示例的对象将作为可观察对象的params参数传递queryParams。如有必要,可以在处理程序中进行 API 调用。

但是:我更喜欢直接在我的组件中处理状态更改并避免绕过ActivatedRoute.queryParams. IMO,导航路由器,让 Angular 执行路由魔术并处理queryParams更改以做某事,完全混淆了我的组件中发生的关于我的代码的可维护性和可读性的事情。我做什么:

将传入queryParamsobservable 的状态与我组件中的当前状态进行比较,如果它没有在那里发生变化,则什么都不做,而是直接处理状态变化:

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);
   }
}
Run Code Online (Sandbox Code Playgroud)

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

var createHistoryEntry = true // or false
var url = ... // see above
this.router.navigateByUrl(url, { replaceUrl : !createHistoryEntry});
Run Code Online (Sandbox Code Playgroud)

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

这超出了最初的问题,但解决了常见的情况,因此可能很有用:state上面的对象仅限于平面对象(只有简单的 string/bool/int/... 属性但没有嵌套对象的对象)。我发现了这个限制,因为我需要区分需要通过后端调用处理的属性和仅由组件内部使用的其他属性。我想要一个状态对象,如:

var state = { filter: { something: '', foo: 'bar' }, viewSettings: { ... } };
Run Code Online (Sandbox Code Playgroud)

要将此状态用作路由器的 queryParams 对象,需要将其展平。我只是JSON.stringify对象的所有第一级属性:

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;
 }
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

最后:一个随时可用的 Angular 服务

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

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

可以像这样使用:

@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
     }

  // ...

}
Run Code Online (Sandbox Code Playgroud)

不要忘记providers: [QueryParamsService]在组件级别上为组件创建新服务实例的声明。不要在 app 模块上全局注册服务。