Query params always purport to be empty on page load even though provided
  • 27-Apr-2023
Lightrun Team
Author Lightrun Team
Share
Query params always purport to be empty on page load even though provided

Query params always purport to be empty on page load even though provided

Lightrun Team
Lightrun Team
27-Apr-2023

Explanation of the problem

This is a bug report. When using the injected ActivatedRoute’s queryParams property in an Angular 2.0.X component to retrieve query parameters from a URL, the property fires twice when query parameters are present. The first time with an empty object, and the second time with the actual query parameters. This behavior is unexpected as the subscription should only fire once with the provided query parameters.

To reproduce the issue, inject the ActivatedRoute in a component, and call it “route” in the constructor. Then, use the route.queryParams property to retrieve query parameters from the URL. Use the filter operator to check if a specific query parameter, say “foo,” is present in the URL. If “foo” is not present, the subscription should show an alert message. Navigate to the path “/?foo=bar,” and the alert message will show even though the “foo” query parameter is present in the URL. This issue occurs in both the constructor and ngOnInit methods.

The motivation for changing this behavior is to avoid confusion and potential bugs in the application. Currently, the subscription falsely shows that there is no query parameter when there is one. This can lead to unexpected behavior in the application. The current workaround is to use the debounceTime operator, but this introduces race conditions. Therefore, fixing the behavior to only fire once with the provided query parameters would be beneficial.

Environment:

The operating system used is macOS Sierra version 10.12. The IDE used is Atom 1.10.2, and the package manager is NPM. The Angular version used is 2.0.X 2.0.0. The browser used is Chrome version 53.0.2785.143 (64-bit) and Safari version 10.0 (12602.1.50.0.10). The language used is TypeScript version 1.8.10. The Node version used is 4.0.0, although it is not clear what AoT means.

Troubleshooting with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for FormGroup.disable() and FormGroup.enable() do not allow resetting disabled state in Angular Angular

The question at hand deals with an issue where the query parameters are not being recognized accurately by the ActivatedRoute in Angular. According to one answer, this is because the instantiation of the root component is static and not controlled by the router. As a result, the activated route has to exist regardless of the URL, which means that the empty state is always allowed. To avoid race conditions, the recommended workaround is to skip the first emission and subscribe to the subsequent ones using “.skip(1)”.

Another answer suggests that using a BehaviorSubject can be a good approach, but the initial value is incorrect when the page is opened with query params. The initial value should accurately reflect the current state of the URL, and therefore, the first thing that should be shown is the query params that are present. Otherwise, it becomes difficult to reliably determine if the page was opened without query params, without introducing a clunky workaround using debounceTime.

Overall, the key takeaway is that there is no straightforward solution to this issue as the root component’s instantiation and the activated route’s existence are fundamental to the router’s functioning. However, developers can use the recommended workarounds to ensure that query params are recognized accurately, and initial values of BehaviorSubjects are set correctly.

Other popular problems with Angular

Problem: Change Detection Performance

One of the most common problems with Angular is related to change detection performance. Angular uses a mechanism called change detection to update the view when the component’s data changes. However, if the component has a lot of bindings or the change detection runs too often, the performance can suffer. This can lead to slow rendering times and a poor user experience.

Solution:

One solution to this problem is to use the OnPush strategy for change detection. This strategy only runs change detection when an input property of the component changes, instead of running it for all bindings. To use the OnPush strategy, you need to set the changeDetection property of the component to ChangeDetectionStrategy.OnPush.

@Component({
  selector: 'app-my-component',
  template: '...',
  changeDetection: ChangeDetectionStrategy.OnPush
})

Another solution is to use the async pipe for bindings that are observables. This pipe automatically subscribes to the observable and unsubscribes when the component is destroyed, and also triggers change detection only when the observable emits a new value.

@Component({
  selector: 'app-my-component',
  template: `
    <div *ngFor="let item of items | async"></div>
  `
})
export class MyComponent {
  items: Observable<Item[]> = this.service.getItems();
}

Problem: Memory Leaks

Another common problem with Angular is related to memory leaks. Memory leaks occur when an object is no longer in use but is still being held in memory by other objects. This can happen when a component is not properly unsubscribed from observables or event emitters, or when a component is not properly cleaned up during the destruction process.

Solution:

To avoid memory leaks, it is important to unsubscribe from observables and event emitters when a component is destroyed. One way to do this is to use the ngOnDestroy lifecycle hook. In this hook, you can call the unsubscribe method of the subscription or use takeUntil operator.

export class MyComponent implements OnInit, OnDestroy {
  private unsubscribe$ = new Subject<void>();

  ngOnInit() {
    this.service.getItems()
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe(items => this.items = items);
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

Another way to avoid memory leaks is to use the async pipe for observables and event emitters. This pipe automatically unsubscribes when the component is destroyed.

<div *ngFor="let item of items | async"></div>

Problem: Routing and Navigation

Routing and navigation is one of the most important parts of any single-page application, and it’s also one of the most common sources of problems in Angular. Issues can range from routing configuration to navigation events and guards.

Solution:

One solution to this problem is to use the Angular Router module, which provides a powerful and flexible way to handle routing and navigation. This module provides a powerful way to configure routes and navigate between different parts of your application.

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Another solution is to use guards. Guards are classes that can be used to protect access to certain routes. They can be used to prevent a user from navigating to a route or to prompt the user for confirmation before navigating.

@Injectable()
export class CanActivateGuard implements CanActivate {
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    // code to check if the user is allowed to activate the route
    return true;
  }
}

In the routing configuration, the guard can be added to a route to protect it.

const routes: Routes = [
  { path: 'secret', component: SecretComponent, canActivate: [CanActivateGuard] }
];

This way the routing and navigation in the application can be handled properly and also it’s possible to restrict access to certain routes based on certain conditions.

A brief introduction to Angular

Angular is a JavaScript framework for building web applications. It is built on top of the popular JavaScript framework, AngularJS. Angular is a complete rewrite of AngularJS and is designed to be more efficient, modular, and easier to use. It is also built with TypeScript, a superset of JavaScript that provides static typing and other features that make it easier to write and maintain large applications.

Angular uses a component-based architecture, where an application is composed of a tree of components. Each component is responsible for a specific part of the application’s UI and logic. Components can also be nested, allowing for a hierarchical and modular structure. Angular also provides a powerful template language that allows developers to declaratively describe the UI of a component. The template language is tightly integrated with the component’s logic, making it easy to create complex and interactive UIs. Angular also provides a powerful set of directives and pipes that can be used to manipulate the DOM and transform data. The framework also provides a powerful set of services that can be used to share data and logic across the application.

Most popular use cases for Angular

  1. Building Single-Page Applications (SPAs): Angular can be used to build Single-Page Applications (SPAs). SPAs are web applications that load a single HTML page and dynamically update the content as the user interacts with the app. Angular provides a powerful set of tools for building SPAs, including a powerful router for handling client-side routing and navigation, and a powerful template language for describing the UI of the application.
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
  1. Building Progressive Web Applications (PWAs): Angular can be used to build Progressive Web Applications (PWAs). PWAs are web applications that can be installed on the user’s device and run offline. Angular provides a powerful set of tools for building PWAs, including a powerful service worker module that can be used to cache assets and provide offline support.
  2. Building Mobile Applications: Angular can be used to build mobile applications using technologies like Apache Cordova or NativeScript. This allows developers to create mobile apps using web technologies like HTML, CSS, and JavaScript, and then package them for various platforms such as iOS and Android. Angular provides a powerful set of tools for building mobile apps, including a powerful layout system that can be used to adapt the app’s UI to different screen sizes and resolutions.
Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.