Angular:配置默认的 QueryParamsHandling [英] Angular: configure default QueryParamsHandling
问题描述
我已经构建了一个 angular 9 应用程序,并使用 @ngx-translate 添加了本地化.我已经配置了我的应用程序,以便它采用 lang
查询参数并相应地更改区域设置.
@Component({选择器:'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.scss']})导出类 AppComponent 实现 OnInit, OnDestroy {构造函数(私有路由:ActivatedRoute,私有translateService:TranslateService){this.translateService.setDefaultLang('en');this.route.queryParamMap.subscribe((params) => {让 lang = params.get('lang');console.log('语言', lang);如果(语言!== 空){this.translateService.use(lang);}});}}
然后我在侧边栏上添加了 3 个按钮来更改查询参数(并切换语言)
<a [routerLink]='[]' [queryParams]="{}"><app-flag [country]="'en'" [appHoverClass]="'brightness-250'"></app-flag></a><a [routerLink]='[]' [queryParams]="{'lang':'nl'}"><app-flag [country]="'nl'" [appHoverClass]="'brightness-250'"></app-flag></a><a [routerLink]='[]' [queryParams]="{'lang':'fr'}"><app-flag [country]="'fr'" [appHoverClass]="'brightness-250'"></app-flag></a>
这工作正常.但是当一个普通的 routerLink 被按下,或者在一个 router.navigate() 调用时,查询参数再次丢失.
我不想用 [queryParamsHandling]="'preserve'"
指令装饰我的应用程序中的每一个 routerLink
,因为这是一项乏味的工作并且可怕的做法.这个主题已经有一个 GitHub 问题活跃,但 angular 团队几乎没有研究它(已经 4 年了):https://github.com/angular/angular/issues/12664
有没有办法(任何方式)在导航时默认保留查询参数(或只是 lang
查询参数)?
我已经在默认的角度路由器之上创建了一个 ExtendedRouter
import { Router, QueryParamsHandling, NavigationExtras, UrlTree } from '@angular/router';导出类 ExtendedRouter {构造函数(私有路由器:路由器){}私人 _defaultQueryParamsHandling: QueryParamsHandling = null;公共获取 defaultQueryParamsHandling() {返回 this._defaultQueryParamsHandling;}公共设置 defaultQueryParamsHandling(值:QueryParamsHandling){this._defaultQueryParamsHandling = 值;}公共导航(命令:任何[],额外的?:NavigationExtras){返回 this.router.navigate(命令,{queryParamsHandling: extras.queryParamsHandling ??this.defaultQueryParamsHandling ??'',片段:extras.fragment,保留片段:额外的.preserveFragment,queryParams: extras.queryParams,相对:extras.relativeTo,replaceUrl: extras.replaceUrl,skipLocationChange: extras.skipLocationChange});}public navigateByUrl(url: string | UrlTree, extras?: NavigationExtras) {返回 this.router.navigateByUrl(url, {queryParamsHandling: extras.queryParamsHandling ??this.defaultQueryParamsHandling ??'',片段:extras.fragment,保留片段:额外的.preserveFragment,queryParams: extras.queryParams,相对:extras.relativeTo,replaceUrl: extras.replaceUrl,skipLocationChange: extras.skipLocationChange});}public createUrlTree(命令:任何[],额外的?:NavigationExtras){返回 this.router.createUrlTree(commands, extras);}公共序列化Url(网址:UrlTree){返回 this.router.serializeUrl(url);}}
但这与 [routerLink]
指令无关.我也尝试过创建一个,但我需要的所有字段都限定为 private
.
import { Directive, Renderer2, ElementRef, Attribute, Input } from '@angular/core';从@angular/router"导入 { RouterLink, Router, ActivatedRoute };从'../../helpers/extended-router'导入{扩展路由器};@指示({选择器:'[extendedRouterLink]'})导出类 ExtendedRouterLinkDirective 扩展 RouterLink {私有路由器2:路由器;私有路由2:ActivatedRoute;私有命令 2: any[] = [];构造函数(路由器:路由器,路由:ActivatedRoute,@Attribute('tabindex')tabIndex:字符串,渲染器:Renderer2,el:ElementRef<any>,私有extendedRouter:ExtendedRouter){超级(路由器,路由,tabIndex,渲染器,el);this.router2 = 路由器;this.route2 = 路线;}@输入()设置extendedRouterLink(commands: any[] | string | null | undefined) {如果(命令!= null){this.commands2 = Array.isArray(commands) ?命令:[命令];} 别的 {this.commands2 = [];}super.commands = 命令;}获取 urlTree() {返回 this.router2.createUrlTree(this.commands, {相对:this.route2,queryParams: this.queryParams,片段:this.fragment,queryParamsHandling: this.queryParamsHandling,保留片段:this.attrBoolValue(this.preserveFragment),});}private attrBoolValue = (s: any) =>{返回 s === '' ||!!s;}}
有没有人知道如何解决这个问题而不必在每个 [routerLink]
上定义一个 [queryParamsHandling]
?
这种方法有一个小问题:
@指示({选择器:'a[routerLink]'})导出类 QueryParamsHandlingDirective 扩展 RouterLinkWithHref {queryParamsHandling: QueryParamsHandling = 'merge';}
问题在于它扩展了 RouterLinkWithHref
,这意味着 <a routerLink="">
将有 2 个指令(一个扩展另一个)附加到它.
还有 这是RouterLinkWithHref
的 click
处理程序中发生了什么:
@HostListener('click')onClick(): 布尔 {常量附加 = {skipLocationChange: attrBoolValue(this.skipLocationChange),replaceUrl: attrBoolValue(this.replaceUrl),状态:this.state,};this.router.navigateByUrl(this.urlTree, extras);返回真;}
更重要的是它在传送到浏览器时的外观:
RouterLinkWithHref.prototype.onClick = function (button, ctrlKey, metaKey, shiftKey) {if (button !== 0 || ctrlKey || metaKey || shiftKey) {返回真;}if (typeof this.target === 'string' && this.target != '_self') {返回真;}变量附加 = {skipLocationChange: attrBoolValue(this.skipLocationChange),replaceUrl: attrBoolValue(this.replaceUrl),状态:this.state};this.router.navigateByUrl(this.urlTree, extras);返回假;};
这意味着当你点击一个 标签时,
QueryParamsHandlingDirective.onClick
将被调用,然后 RouterLinkWithHref.onClick
>.但是由于 RouterLinkWithHref.onClick
是最后调用的,它不会将 queryParamsHandling
设置为 merge
.
解决方案是稍微修改自定义指令,使其不继承任何东西,而只是设置一个属性:
@Directive({选择器:'a[routerLink]'})导出类 QueryParamsHandlingDirective {构造函数(路由器链接:RouterLinkWithHref){routerLink.queryParamsHandling = '合并';}}
I've built an angular 9 app, and added localization with @ngx-translate. I've configured my app so that it takes the lang
query parameter and changes the locale accordingly.
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
constructor(private route: ActivatedRoute, private translateService: TranslateService) {
this.translateService.setDefaultLang('en');
this.route.queryParamMap.subscribe((params) => {
let lang = params.get('lang');
console.log('language', lang);
if (lang !== null) {
this.translateService.use(lang);
}
});
}
}
I then added 3 buttons on my sidebar to change the query parameter (and switch the language)
<div class="p-1 text-center">
<a [routerLink]='[]' [queryParams]="{}">
<app-flag [country]="'en'" [appHoverClass]="'brightness-250'"></app-flag>
</a>
<a [routerLink]='[]' [queryParams]="{'lang':'nl'}">
<app-flag [country]="'nl'" [appHoverClass]="'brightness-250'"></app-flag>
</a>
<a [routerLink]='[]' [queryParams]="{'lang':'fr'}">
<app-flag [country]="'fr'" [appHoverClass]="'brightness-250'"></app-flag>
</a>
</div>
This is working fine. But when a normal routerLink is pressed, or at a router.navigate() call, the query parameters are lost again.
I don't want to decorate each and every routerLink
in my application with the [queryParamsHandling]="'preserve'"
directive since this is a tedious job and horrible practice. There is already a GitHub issue active for this topic, but the angular team is pretty much not working on it (for 4 years already): https://github.com/angular/angular/issues/12664
Is there a way (any way) to have the query parameters (or just the lang
query parameter) preserved by default when navigating?
I've already created an ExtendedRouter
on top of the default angular router
import { Router, QueryParamsHandling, NavigationExtras, UrlTree } from '@angular/router';
export class ExtendedRouter {
constructor(private router: Router) {
}
private _defaultQueryParamsHandling: QueryParamsHandling = null;
public get defaultQueryParamsHandling() {
return this._defaultQueryParamsHandling;
}
public set defaultQueryParamsHandling(value: QueryParamsHandling) {
this._defaultQueryParamsHandling = value;
}
public navigate(commands: any[], extras?: NavigationExtras) {
return this.router.navigate(commands, {
queryParamsHandling: extras.queryParamsHandling ?? this.defaultQueryParamsHandling ?? '',
fragment: extras.fragment,
preserveFragment: extras.preserveFragment,
queryParams: extras.queryParams,
relativeTo: extras.relativeTo,
replaceUrl: extras.replaceUrl,
skipLocationChange: extras.skipLocationChange
});
}
public navigateByUrl(url: string | UrlTree, extras?: NavigationExtras) {
return this.router.navigateByUrl(url, {
queryParamsHandling: extras.queryParamsHandling ?? this.defaultQueryParamsHandling ?? '',
fragment: extras.fragment,
preserveFragment: extras.preserveFragment,
queryParams: extras.queryParams,
relativeTo: extras.relativeTo,
replaceUrl: extras.replaceUrl,
skipLocationChange: extras.skipLocationChange
});
}
public createUrlTree(commands: any[], extras?: NavigationExtras) {
return this.router.createUrlTree(commands, extras);
}
public serializeUrl(url: UrlTree) {
return this.router.serializeUrl(url);
}
}
But this doesn't deal with the [routerLink]
directive. I've tried creating one as well, but all fields I need are scoped to private
.
import { Directive, Renderer2, ElementRef, Attribute, Input } from '@angular/core';
import { RouterLink, Router, ActivatedRoute } from '@angular/router';
import { ExtendedRouter } from '../../helpers/extended-router';
@Directive({
selector: '[extendedRouterLink]'
})
export class ExtendedRouterLinkDirective extends RouterLink {
private router2: Router;
private route2: ActivatedRoute;
private commands2: any[] = [];
constructor(router: Router, route: ActivatedRoute, @Attribute('tabindex') tabIndex: string, renderer: Renderer2, el: ElementRef<any>, private extendedRouter: ExtendedRouter) {
super(router, route, tabIndex, renderer, el);
this.router2 = router;
this.route2 = route;
}
@Input()
set extendedRouterLink(commands: any[] | string | null | undefined) {
if (commands != null) {
this.commands2 = Array.isArray(commands) ? commands : [commands];
} else {
this.commands2 = [];
}
super.commands = commands;
}
get urlTree() {
return this.router2.createUrlTree(this.commands, {
relativeTo: this.route2,
queryParams: this.queryParams,
fragment: this.fragment,
queryParamsHandling: this.queryParamsHandling,
preserveFragment: this.attrBoolValue(this.preserveFragment),
});
}
private attrBoolValue = (s: any) => {
return s === '' || !!s;
}
}
Anyone an idea how to get around this without having to define a [queryParamsHandling]
on each [routerLink]
?
There is a small problem with this approach:
@Directive({
selector: 'a[routerLink]'
})
export class QueryParamsHandlingDirective extends RouterLinkWithHref {
queryParamsHandling: QueryParamsHandling = 'merge';
}
The problem is that it extends RouterLinkWithHref
, meaning an <a routerLink="">
will have 2 directives(one which extends the other) attached to it.
And this is what happens inside RouterLinkWithHref
's click
handler:
@HostListener('click')
onClick(): boolean {
const extras = {
skipLocationChange: attrBoolValue(this.skipLocationChange),
replaceUrl: attrBoolValue(this.replaceUrl),
state: this.state,
};
this.router.navigateByUrl(this.urlTree, extras);
return true;
}
What's more important is how this looks when it's shipped to the browser:
RouterLinkWithHref.prototype.onClick = function (button, ctrlKey, metaKey, shiftKey) {
if (button !== 0 || ctrlKey || metaKey || shiftKey) {
return true;
}
if (typeof this.target === 'string' && this.target != '_self') {
return true;
}
var extras = {
skipLocationChange: attrBoolValue(this.skipLocationChange),
replaceUrl: attrBoolValue(this.replaceUrl),
state: this.state
};
this.router.navigateByUrl(this.urlTree, extras);
return false;
};
This means that when you click on an <a>
tag, the QueryParamsHandlingDirective.onClick
will be invoked, and then RouterLinkWithHref.onClick
. But since RouterLinkWithHref.onClick
is called last, it won't have the queryParamsHandling
set to merge
.
The solution is to slightly modify the custom directive so that it does not inherit anything, but simply sets a property:
@Directive({
selector: 'a[routerLink]'
})
export class QueryParamsHandlingDirective {
constructor (routerLink: RouterLinkWithHref) {
routerLink.queryParamsHandling = 'merge';
}
}
这篇关于Angular:配置默认的 QueryParamsHandling的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!