Angular 角度:配置默认查询参数

Angular 角度:配置默认查询参数,angular,angular9,query-parameters,angular-routerlink,Angular,Angular9,Query Parameters,Angular Routerlink,我已经构建了angular 9应用程序,并使用@ngx translate添加了本地化。我已经配置了我的应用程序,以便它接受langquery参数并相应地更改区域设置 @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit, OnDe

我已经构建了angular 9应用程序,并使用@ngx translate添加了本地化。我已经配置了我的应用程序,以便它接受
lang
query参数并相应地更改区域设置

@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个按钮来更改查询参数(并切换语言)

但这并不涉及
[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;
  }

}
import{Directive,renderr2,ElementRef,Attribute,Input}来自“@angular/core”;
从'@angular/Router'导入{RouterLink,Router,ActivatedRoute};
从“../../helpers/ExtendedRouter”导入{ExtendedRouter};
@指示({
选择器:“[extendedRouterLink]”
})
导出类ExtendedRouterLink指令扩展RouterLink{
专用路由器2:路由器;
专用路由2:激活的路由;
私有命令2:任意[]=[];
构造函数(路由器:路由器,路由:ActivatedRoute,@Attribute('tabindex')tabindex:string,呈现器:render2,el:ElementRef,私有扩展路由器:扩展路由器){
超级(路由器、路由、选项卡索引、渲染器、el);
this.router2=路由器;
this.route2=路线;
}
@输入()
设置extendedRouterLink(命令:任意[]|字符串| null |未定义){
if(命令!=null){
this.commands2=Array.isArray(commands)?commands:[commands];
}否则{
this.commands2=[];
}
super.commands=命令;
}
获取urlTree(){
返回this.router2.createUrlTree(this.commands{
相对人:这条路线2,
queryParams:this.queryParams,
片段:这个片段,
queryParamsHandling:this.queryParamsHandling,
preserveFragment:this.attrBoolValue(this.preserveFragment),
});
}
私有属性布尔值=(s:any)=>{
返回s==''| |!!s;
}
}

任何人都知道如何绕过这个问题,而不必在每个
[routerLink]
上定义
[queryParamsHandling]

您可以将
路由器.navigate()
包装到一个实用程序类中,该类使用一个方法将
路由器本身以及您希望它做的事情作为参数(可以使用可选参数/默认值,或将其传递给对象)并在默认情况下每次添加
queryParamsHandling

此方法存在一个小问题:


@指示({
选择器:“a[routerLink]”
})
导出类QueryParamsHandlingDirective扩展了RouterLinkWithHref{
queryParamsHandling:queryParamsHandling='merge';
}
问题是它扩展了
RouterLinkWithHref
,这意味着
RouterLinkWithHref
中的
单击
处理程序:

@HostListener('click')
onClick():布尔值{
临时演员={
skipLocationChange:attrBoolValue(this.skipLocationChange),
replaceUrl:attrBoolValue(this.replaceUrl),
州:这个州,
};
this.router.navigateByUrl(this.urlTree,extras);
返回true;
}
更重要的是,它在发送到浏览器时的外观:

RouterLinkWithHref.prototype.onClick=函数(按钮、ctrlKey、元键、移位键){
如果(按钮!==0 | | | | | |图元键| |移位键){
返回true;
}
if(typeof this.target==='string'&&this.target!='u self'){
返回true;
}
var额外费用={
skipLocationChange:attrBoolValue(this.skipLocationChange),
replaceUrl:attrBoolValue(this.replaceUrl),
州:这个州
};
this.router.navigateByUrl(this.urlTree,extras);
返回false;
};

这意味着当你最后点击一个

时,我需要一种更灵活的方法,可以保留一个查询参数(lang),但删除另一个(返回url)。因此,最终我决定继续编写自己的
AdvancedRouter
advRouterLink

用于设置配置的提供程序:

import { InjectionToken } from "@angular/core";
import { QueryParamsConfig } from "../../interfaces/query-params-config";

export const QUERY_PARAMS_CONFIG = new InjectionToken<QueryParamsConfig>('QueryParamsConfig');
AppModule
中配置此提供程序:

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    AdvancedRouterModule
  ],
  providers: [{
    provide: QUERY_PARAMS_CONFIG, useValue: <QueryParamsConfig>{
      'lang': 'preserve',
      'return': ''
    }
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }
UrlWithQueryParams

import { Params } from "@angular/router";

export interface UrlWithQueryParams {
    url: string;
    queryParams: Params;
}
AdvancedRouterLink指令

import { LocationStrategy } from '@angular/common';
import { Directive, Input } from '@angular/core';
import { ActivatedRoute, Router, RouterLinkWithHref, UrlTree } from '@angular/router';
import { AdvancedRouter } from '../../services/advanced-router/advanced-router';

// See https://github.com/angular/angular/blob/master/packages/router/src/directives/router_link.ts#L256

@Directive({selector: 'a[advRouterLink],area[advRouterLink]'})
export class AdvancedRouterLinkDirective extends RouterLinkWithHref {

  constructor(
    private advancedRouter: AdvancedRouter,
    private nativeRoute: ActivatedRoute,
    nativeRouter: Router,
    nativeLocationStrategy: LocationStrategy
  ) {
    super(nativeRouter, nativeRoute, nativeLocationStrategy);
  }

  private nativeCommands: any[] = [];

   @Input()
   set advRouterLink(commands: any[] | string | null | undefined) {
     if (commands != null) {
       this.nativeCommands = Array.isArray(commands) ? commands : [commands];
     } else {
       this.nativeCommands = [];
     }
   }

  get urlTree(): UrlTree {
    return this.advancedRouter.createUrlTree(this.nativeCommands, {
      relativeTo: this.relativeTo !== undefined ? this.relativeTo : this.nativeRoute,
      queryParams: this.queryParams,
      fragment: this.fragment,
      queryParamsHandling: '', // Drop queryparams and let the AdvancedRouter do all the work
      preserveFragment: this.attrBoolValue(this.preserveFragment),
    });
  }
  
  private attrBoolValue(s: any) {
    return s === '' || !!s;
  }
}
  • Github回购:
  • 节点包:

这就是我使用扩展路由器类的地方,但是我不能处理
routerLink
指令。这就是我被卡住的地方……这似乎是一个很好的解决方案:的确,我一定已经看过了。我会尝试它,它确实工作得很好。很好的回答似乎在使用角度路由时,该指令不适用于lin由于某些原因,新创建的组件中有k个元素。你知道解决此问题的方法吗?很抱歉,这是正常的演示。这是一个扩展版本,其中包含路由组件内的链接:工作非常好。非常好且快速的解决方案。感谢您的深入回答。我很高兴能提供帮助!祝您好运!
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    AdvancedRouterModule
  ],
  providers: [{
    provide: QUERY_PARAMS_CONFIG, useValue: <QueryParamsConfig>{
      'lang': 'preserve',
      'return': ''
    }
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }
import { Inject, Injectable } from '@angular/core';
import { HttpParams } from '@angular/common/http';
import { ActivatedRoute, NavigationBehaviorOptions, NavigationExtras, Params, Router, UrlCreationOptions, UrlTree } from '@angular/router';
import { QueryParamsConfig } from '../../interfaces/query-params-config';
import { QUERY_PARAMS_CONFIG } from '../../providers';
import { UrlWithQueryParams } from './url-with-query-params';

@Injectable({
  providedIn: 'root'
})
export class AdvancedRouter {

  constructor(private router: Router, private route: ActivatedRoute, @Inject(QUERY_PARAMS_CONFIG) private queryParamsConfig: QueryParamsConfig) {
  }

  public navigate(commands: any[], extras?: NavigationExtras) {
    const newParams = this.computeQueryParameters(this.route.snapshot.queryParams, extras?.queryParams);
    return this.router.navigate(commands, { ...extras, queryParams: newParams });
  }

  public navigateByUrl(url: string | UrlTree, extras?: NavigationBehaviorOptions) {
    // The requested url.
    let urlValue = url instanceof UrlTree
      ? this.router.serializeUrl(url)
      : url;

    // The requested query parameters.
    const requestedParams = this.extractQueryParametersFromUrl(urlValue);

    // Use the current queryparams and requested queryparams
    // to compute the new parameters according to the configuration.
    const newParams = this.computeQueryParameters(this.route.snapshot.queryParams, requestedParams.queryParams);
    const newParamKeys = Object.keys(newParams).filter(key => !['encoder', 'map'].includes(key));
    const newQueryString = newParamKeys.map(key => `${key}=${newParams[key]}`).join('&');

    const newUrl = newParamKeys.length === 0
      ? requestedParams.url
      : `${requestedParams.url}?${newQueryString}`;

    return this.router.navigateByUrl(newUrl, extras);
  }
  
  public createUrlTree(commands: any[], extras?: UrlCreationOptions) {
    const newParams = this.computeQueryParameters(this.route.snapshot.queryParams, extras?.queryParams);
    return this.router.createUrlTree(commands, { ...extras, queryParams: newParams });
  }

  public serializeUrl(url: UrlTree) {
    return this.router.serializeUrl(url);
  }

  private extractQueryParametersFromUrl(url: string) : UrlWithQueryParams {
    if (url.includes('?')) {
      const parts = url.split('?');
      return {
        url: parts[0],
        queryParams: new HttpParams({ fromString: parts[1] })
      };
    } else {
      return {
        url: url,
        queryParams: new HttpParams()
      };
    }
  }

  private containsKey(params: Params, key: string) {
    return Object.keys(params).indexOf(key) > -1;
  }

  private computeQueryParameters(currentParams: Params, requestedParams: Params | null | undefined) {
    // Allow a null object to be passed to this method.
    const newRequestedParams = requestedParams ?? { };

    // Merge the set of keys.
    const allParamKeys = Object.keys({
      ...currentParams,
      ...newRequestedParams
    });

    return <Params>Object.assign({}, ...allParamKeys.map(k => {
        // Compute new value for each Query parameter.
        return {
          key: k,
          value: this.getQueryParameterValue(currentParams, newRequestedParams, k)
        };
      })
      // Remove query parameters to drop.
      .filter(p => p.value !== null)
      // ToDictionary
      .map(p => {
        return { [p.key] : p.value };
      })
    );
  }

  private getQueryParameterValue(currentParams: Params, requestedParams: Params, key: string) {
    switch (this.queryParamsConfig[key]) {
      case 'preserve':
        // Take requested value if present, else take current.

        // Must use containsKey since one may want to explicitly pass a null value for a specific parameter,
        // in order to drop the query parameter specified.
        return Object.keys(requestedParams).indexOf(key) === -1
          ? currentParams[key]
          : requestedParams[key];
      case 'merge':
        if (this.containsKey(currentParams, key)) {
          if (this.containsKey(requestedParams, key)) {
            // Query parameter present in both. Merge both values.
            return `${currentParams[key]},${requestedParams[key]}`;
          } else {
            // Query parameter only present in activated route.
            return currentParams[key];
          }
        } else {
          if (this.containsKey(requestedParams, key)) {
            // Query parameter only present in requested list.
            return requestedParams[key];
          } else {
            // Never occurs
          }
        }
        break;
      default:
        // Default is drop query parameter.
        if (this.containsKey(requestedParams, key)) {
          // If still present in requested list, return this value.
          return requestedParams[key];
        } else {
          // Drop query parameter.
          return null;
        }
    }
  }
}
import { Params } from "@angular/router";

export interface UrlWithQueryParams {
    url: string;
    queryParams: Params;
}
import { LocationStrategy } from '@angular/common';
import { Directive, Input } from '@angular/core';
import { ActivatedRoute, Router, RouterLinkWithHref, UrlTree } from '@angular/router';
import { AdvancedRouter } from '../../services/advanced-router/advanced-router';

// See https://github.com/angular/angular/blob/master/packages/router/src/directives/router_link.ts#L256

@Directive({selector: 'a[advRouterLink],area[advRouterLink]'})
export class AdvancedRouterLinkDirective extends RouterLinkWithHref {

  constructor(
    private advancedRouter: AdvancedRouter,
    private nativeRoute: ActivatedRoute,
    nativeRouter: Router,
    nativeLocationStrategy: LocationStrategy
  ) {
    super(nativeRouter, nativeRoute, nativeLocationStrategy);
  }

  private nativeCommands: any[] = [];

   @Input()
   set advRouterLink(commands: any[] | string | null | undefined) {
     if (commands != null) {
       this.nativeCommands = Array.isArray(commands) ? commands : [commands];
     } else {
       this.nativeCommands = [];
     }
   }

  get urlTree(): UrlTree {
    return this.advancedRouter.createUrlTree(this.nativeCommands, {
      relativeTo: this.relativeTo !== undefined ? this.relativeTo : this.nativeRoute,
      queryParams: this.queryParams,
      fragment: this.fragment,
      queryParamsHandling: '', // Drop queryparams and let the AdvancedRouter do all the work
      preserveFragment: this.attrBoolValue(this.preserveFragment),
    });
  }
  
  private attrBoolValue(s: any) {
    return s === '' || !!s;
  }
}