title: Angular: configure default QueryParamsHandling
body: <p>I've built an angular 9 app, and added localization with @ngx-translate. I've configured my app so that it takes the <code>lang</code> query parameter and changes the locale accordingly.</p>
  
  <pre><code>@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) =&gt; {
        let lang = params.get('lang');
        console.log('language', lang);
        if (lang !== null) {
          this.translateService.use(lang);
        }
      });
    }
  }
  </code></pre>
  
  <p>I then added 3 buttons on my sidebar to change the query parameter (and switch the language)</p>
  
  <pre><code>&lt;div class="p-1 text-center"&gt;
    &lt;a [routerLink]='[]' [queryParams]="{}"&gt;
      &lt;app-flag [country]="'en'" [appHoverClass]="'brightness-250'"&gt;&lt;/app-flag&gt;
    &lt;/a&gt;
    &lt;a [routerLink]='[]' [queryParams]="{'lang':'nl'}"&gt;
      &lt;app-flag [country]="'nl'" [appHoverClass]="'brightness-250'"&gt;&lt;/app-flag&gt;
    &lt;/a&gt;
    &lt;a [routerLink]='[]' [queryParams]="{'lang':'fr'}"&gt;
      &lt;app-flag [country]="'fr'" [appHoverClass]="'brightness-250'"&gt;&lt;/app-flag&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>This is working fine. But when a normal routerLink is pressed, or at a router.navigate() call, the query parameters are lost again.</p>
  
  <p>I don't want to decorate each and every <code>routerLink</code> in my application with the <code>[queryParamsHandling]="'preserve'"</code> 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): <a href="https://github.com/angular/angular/issues/12664" rel="nofollow noreferrer">https://github.com/angular/angular/issues/12664</a></p>
  
  <p>Is there a way (any way) to have the query parameters (or just the <code>lang</code> query parameter) preserved by default when navigating?</p>
  
  <p>I've already created an <code>ExtendedRouter</code> on top of the default angular router</p>
  
  <pre><code>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);
    }
  }
  </code></pre>
  
  <p>But this doesn't deal with the <code>[routerLink]</code> directive. I've tried creating one as well, but all fields I need are scoped to <code>private</code>.</p>
  
  <pre><code>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&lt;any&gt;, 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) =&gt; {
      return s === '' || !!s;
    }
  
  }
  </code></pre>
  
  <p>Anyone an idea how to get around this without having to define a <code>[queryParamsHandling]</code> on each <code>[routerLink]</code>?</p>