角度:配置默认的QueryParamsHandling [英] Angular: configure default QueryParamsHandling

查看:83
本文介绍了角度:配置默认的QueryParamsHandling的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我构建了一个angular 9应用程序,并使用@ ngx-translate添加了本地化.我已经配置了我的应用程序,以便它使用 lang 查询参数并相应地更改语言环境.

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

然后我在侧边栏上添加了3个按钮来更改查询参数(并切换语言)

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>

这很好.但是当按下普通的routerLink或在router.navigate()调用时,查询参数将再次丢失.

This is working fine. But when a normal routerLink is pressed, or at a router.navigate() call, the query parameters are lost again.

我不想使用 [queryParamsHandling] ='preserve'" 指令来修饰应用程序中的每个 routerLink ,因为这是一项繁琐的工作,并且可怕的做法.这个话题已经有一个活跃的GitHub问题,但是Angular团队几乎没有对此进行任何工作(已经有四年了): https://github.com/angular/angular/issues/12664

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

是否有一种方法(任何方式)在导航时默认保留查询参数(或只是 lang 查询参数)?

Is there a way (any way) to have the query parameters (or just the lang query parameter) preserved by default when navigating?

我已经在默认的角路由器上创建了 ExtendedRouter

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

但这与 [routerLink] 指令无关.我也尝试过创建一个,但是我需要的所有字段都限定为 private .

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

}

有人知道如何解决此问题而不必在每个 [routerLink] 上定义 [queryParamsHandling] 吗?

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

问题在于它扩展了 RouterLinkWithHref ,这意味着< a routerLink ="> 将具有 2个指令(其中一个扩展另一个).

The problem is that it extends RouterLinkWithHref, meaning an <a routerLink=""> will have 2 directives(one which extends the other) attached to it.

这是在 RouterLinkWithHref click 处理程序中发生什么:

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

这意味着,当您单击< a> 标记时,将调用 QueryParamsHandlingDirective.onClick ,然后 RouterLinkWithHref.onClick .但是,由于最后调用了 RouterLinkWithHref.onClick ,因此不会将 queryParamsHandling 设置为 merge .

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

查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆