我正在使用Angular 2,Google Maps等技术制作一个房地产网站。当用户更改地图的中心位置时,我会向API执行搜索,指示地图当前位置以及半径。问题是,我想在不重新加载整个页面的情况下将这些值反映在URL中。这可能吗? 我找到了一些使用AngularJS 1.x的解决方案,但没有关于Angular 2的。
我正在使用Angular 2,Google Maps等技术制作一个房地产网站。当用户更改地图的中心位置时,我会向API执行搜索,指示地图当前位置以及半径。问题是,我想在不重新加载整个页面的情况下将这些值反映在URL中。这可能吗? 我找到了一些使用AngularJS 1.x的解决方案,但没有关于Angular 2的。
从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/");
}
}
Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'some/newstate'
。 - Inigo您可以使用location.go(url)
,它会改变您的URL,而不会更改应用程序的路由。
注意:这可能会引起其他效果,例如从当前路由重定向到子路由。
相关问题描述了location.go
,但不会通知Router
进行更改。
import { Location } from '@angular/common';
constructor(private _location: Location){ }
updateUrl(url: string) {
this._location.go(url)
}
import { Location } from '@angular/common';
表示从@angular/common
模块导入Location
。 - tehCivilianconstructor(private location: Location){ }
这样的构造函数? - Pankaj Parkarlocation.go()
,而应该键入 this.location.go()
。加上 this.
之后,您调用了 TypeScript 的 location 接口。 - georg-un使用 location.go(url)
是可行的方式,但不要硬编码URL,考虑使用 router.createUrlTree()
生成URL。
如果您想进行以下路由调用:this.router.navigate([{param: 1}], {relativeTo: this.activatedRoute})
,但不想重新加载组件,则可以重写为:
const url = this.router.createUrlTree([], {relativeTo: this.activatedRoute, queryParams: {param: 1}}).toString()
this.location.go(url);
createUrlTree(commands: any[], navigationExtras?: NavigationExtras)
的声明。您需要使用位于 navigationExtras
中的 queryParams
,而不是 commands
。 createUrlTree([], {relativeTo: this.activatedRoute, queryParams: {param: 1} })
- kitthis.router.createUrlTree([], {relativeTo: this.activatedRoute, queryParams: {param: 1}}).toString()
- Eyad Arafatlocation.go
后,this.activatedRoute
不会改变,所以您也将在旧路由上添加参数... - Uko// 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' } });
我在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' );
}
}
以下是关于该问题的Angular资源: https://angular.io/docs/ts/latest/api/common/index/Location-class.html https://angular.io/docs/ts/latest/api/common/index/LocationStrategy-class.html
我曾经有过与问题描述类似的需求,但是根据现有答案花费了一段时间才找到解决方案,所以我想分享我的最终解决方案。
需求
我的视图状态(组件,在技术上)可以被用户更改(过滤设置、排序选项等)。当状态更改发生时,即用户更改排序方向时,我想要:
此外,我还希望:
解决方案:在不重新加载组件的情况下更改状态
使用路由参数或查询参数进行状态更改时,状态更改不会导致组件重新加载。组件实例保持活动状态。我认为使用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
对象将作为queryParams
可观察参数的params
参数传递。在处理程序中,如果必要,可以进行API调用。ActivatedRoute.queryParams
进行绕路。我的个人看法是,导航路由,让Angular执行路由魔法并处理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});
解决方案:将复杂对象作为状态
这超出了原始问题的范围,但涉及常见场景,因此可能会有用:上面的state
对象仅限于平面对象(仅具有简单的字符串/布尔/整数等属性但没有嵌套对象)。我发现这是有限制的,因为我需要区分需要通过后端调用处理的属性和其他仅由组件内部使用的属性。我想要一个像这样的状态对象:
var state = { filter: { something: '', foo: 'bar' }, viewSettings: { ... } };
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;
}
同时,在处理路由器返回的查询参数时,请注意:
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;
}
最终:一个可直接使用的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;
}
}
可用于:
@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
}
// ...
}
不要忘记在组件级别上添加 providers: [QueryParamsService]
语句以创建该组件的新服务实例。不要在应用程序模块上全局注册该服务。
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
中,所以当你切换语言时,你就不会像在this.location.replaceState()
中遇到的问题一样。URL
,而不会使你的应用程序更加复杂。this.router.navigate([], {
queryParams: this.queryParams,
queryParamsHandling: 'merge',
replaceUrl: true,
});
对于我来说,使用Angular 4.4.5时出现了一个混合的情况。
使用router.navigate时,它不尊重相对于activatedRoute的部分,从而摧毁了我的URL。
最后我采用了以下方式:
this._location.go(this._router.createUrlTree([this._router.url], { queryParams: { profile: value.id } }).toString())
createUrlTree
创建URL树,并使用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());
replaceState
方法而不是 go
方法,这样它就会替换先前的状态而不是添加到它之后。当您有以 /new
结尾的 URL,然后想要在将其保存到数据库后将 new
替换为实体 ID 时,这个方法非常方便。 - Kirill G.
window.history.pushState("http://example.ca", "示例标题", "/example/path.html");
- Dean Van Greunen