我已经建立了一个Angular 9的应用程序,并添加了本地化与@ngx-translate。我已经配置了我的应用程序,使它采取的是 lang
查询参数,并相应地改变语言环境。
@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);
}
});
}
}
然后我在侧栏上添加了3个按钮来改变查询参数(并切换语言)。
<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>
这样做很好。但是当按下普通的routerLink,或者在调用router.navigate()时,查询参数又丢失了。
我不想把每一个的 routerLink
在我的申请中,用 [queryParamsHandling]="'preserve'"
指令,因为这是一项繁琐的工作,也是可怕的做法。已经有一个GitHub问题活跃在这个主题上,但angular团队几乎没有在做这件事(已经4年了)。https:/github.comangularangularissues12664
有没有一种方法(任何方法)可以让查询参数(或仅仅是 lang
查询参数)在导航时默认保存?
我已经创建了一个 ExtendedRouter
在默认的Angular路由器上
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);
}
}
但这并没有处理好 [routerLink]
指令。我也试着创建了一个,但所有我需要的字段都被限定在了 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;
}
}
有谁知道如何解决这个问题而不需要定义一个 [queryParamsHandling]
在每个 [routerLink]
?
这种方法有一个小问题。
@Directive({
selector: 'a[routerLink]'
})
export class QueryParamsHandlingDirective extends RouterLinkWithHref {
queryParamsHandling: QueryParamsHandling = 'merge';
}
问题是它扩展了 RouterLinkWithHref
意思是 <a routerLink="">
将有 2项指令(一者延伸另一者)附于其上。
而 这是怎么回事 里面 RouterLinkWithHref
's click
处理程序。
@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;
}
更重要的是当它被运到浏览器时的样子。
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;
};
这意味着当你点击一个 <a>
标签,该 QueryParamsHandlingDirective.onClick
将被调用,然后 RouterLinkWithHref.onClick
. 但由于 RouterLinkWithHref.onClick
被称为最后一个,它将不会有 queryParamsHandling
设为 merge
.
解决办法是稍微修改一下自定义指令,使其能够做到 不继承 任何东西,但只是简单地设置一个属性。
@Directive({
selector: 'a[routerLink]'
})
export class QueryParamsHandlingDirective {
constructor (routerLink: RouterLinkWithHref) {
routerLink.queryParamsHandling = 'merge';
}
}
你可以把它包起来 router.navigate()
变成一个实用类,该实用类有一个方法,其参数为 router
本身和你想让它做的事情(可能有可选的参数默认值,或者传递给它一个对象),并在每次默认的情况下添加 queryParamsHandling
.