"Once the queryParams have been updated, the ActivatedRoute.queryParams event is triggered once

Within my Angular component, I am making an API call by passing a hash string extracted from the current query parameters. Upon receiving the API result, a new hash is also obtained and set as the new hash query parameter. Subsequently, the next API call will be made with this new hash value. A simplified (mock) version of the code snippet is presented below:

import { Component} from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Observable, filter, first, mergeMap, of } from 'rxjs';

export class ResourceTableComponent{
  constructor(
    private readonly route: ActivatedRoute,
    private readonly router: Router
  ) {}

  protected hash = firstValueFrom(
    this.route.queryParams.pipe(
      filter((params) => 'hash' in params),
      mergeMap((params) => this.fetch(params['hash'])),
      mergeMap((hash) =>
        this.setQueryParams({ hash }).then(() => hash)
      )
    )
  );

  protected fetch(hash: string): Observable<string> {
    console.log('fetch', hash);
    return of(Date.now().toString());
  }

  private setQueryParams(queryParams: Params): Promise<boolean> {
    return this.router.navigate([], {
      queryParams,
      queryParamsHandling: 'merge',
    });
  }
}

The issue arises where the fetch function is called four times as evidenced by the following logged output:

fetch 1691227799752
fetch 1691228130070
fetch 1691228130110
fetch 1691228130131

The desired behavior is for fetch to be invoked only once, update the hash query parameter with the result, and trigger subsequent calls to fetch only upon page reload or external call.

To achieve this, using first() is one possible solution:

this.route.queryParams.pipe(
  filter((params) => 'hash' in params),
  first(),                                              // <- rxjs first()
  mergeMap((params) => this.fetch(params['hash'])),
  mergeMap((hash) =>
    this.setQueryParams({ hash }).then(() => hash)
  )
)

However, it does raise the question of why such intervention is necessary. While acknowledging that queryParams behaves as a BehaviorSubject, considerations have been made regarding its emission cycle to comprehend the reason for the observed scenario involving multiple calls.

Answer №1

Essentially, the function this.setQueryParams triggers an update to this.route.queryParams. Typically, since rxjs streams are synchronous, all synchronous operations are performed immediately, leading to the repeated execution of this.setQueryParams.

Why does it stop at 4? The exact reason can vary based on the specific code, API response times, and the time taken for asynchronous route changes to be executed. The process halts once the stream emits its first value, which is then consumed with the assistance of the firstValueFrom helper function. This results in the entire stream unsubscribing and terminating - meaning no further calls are made.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for ensuring the correct typing of a "handler" search for a "dispatcher" style function

Imagine having a structure like this: type TInfoGeneric<TType extends string, TValue> = { valueType: TType, value: TValue, // Correspond to valueType } To prevent redundancy, a type map is created to list the potential valueType and associate i ...

How can I simulate event data while testing a function that is triggered when an event is emitted from another component in Angular integration testing?

Within component A, there is a function responsible for updating the view based on data emitted from component B. The aim is to simply call this function and pass the data without integrating with component B as it would be too complicated for this particu ...

retrieve data types from an array of object values

I am looking to extract types from an array of objects. const names = [ { name: 'Bob' }, { name: 'Jane' }, { name: 'John' }, { name: 'Mike' }, ] The desired result should resemble thi ...

Choose all the checkboxes that use Knockout JS

Struggling with implementing a "select all" checkbox feature as a Junior developer on a complex project utilizing knockout.Js and Typescript. I can't seem to figure out how to select all existing checkboxes. Here is the HTML: <td> <inp ...

Can you explain the distinction between the navigate function and routerLink feature in Angular 2?

As I go through the official Angular 2 tutorial, I noticed that it demonstrates using the navigate function in a way similar to routerLink. Can you explain the differences between these two methods and when it is best to use each? this.router.navigate([ ...

What is the best way to implement an interface for accurately checking each prop type?

Currently, while working with Typescript, I am looking for a solution to define an interface with specific properties inside my object of marks. At the moment, I am using "any", but I know there must be a better approach. Any guidance or advice on how to p ...

The system encountered an issue: Unhandled error: TypeError - the variable "users" has not been defined

I encountered an issue within this Component that is not being detected by the command prompt. Dashboard.component.ts import { Component, OnInit } from '@angular/core'; import { User } from './user.component'; import { UserService ...

Creating Meta tags for Dynamic Routes in a Nuxt 3 Build

I recently encountered an issue when trying to implement dynamic OpenGraph meta tags on a dynamically generated route in Nuxt 3 (and Vue 3). I attempted to set the meta tags dynamically using Javascript, as it was the only dynamic option supported by Nuxt ...

Adding total stars options seems to be causing issues with the star rating feature in the ng framework

I have implemented the ng-starrating library in my Angular project by following this link. It was working fine without specifying the totalstars option like this: <star-rating value="{{rate}}" checkedcolor="#000000" uncheckedcolor="#ffffff"> Howeve ...

Warning: Angular Compilation Issue - Deprecation Advisory

After upgrading to Angular 13, I've been encountering this deprecation warning. TypeError: message.trim is not a function at Function.Rule.FAILURE_STRING (/home/app/node_modules/tslint/lib/rules/deprecationRule.js:30:81) at cb (/home/app/node_modules ...

Angular universal triggers an "Error at XMLHttpRequest.send" issue

After updating my project to Angular 10 and incorporating angular universal, I encountered a strange error. While the application builds without any issues, I face an error when trying to run it on my development environment: ERROR Error at XMLHttpReque ...

What is a more efficient way to differentiate a group of interfaces using an object map instead of using a switch statement?

As someone still getting the hang of advanced typescript concepts, I appreciate your patience. In my various projects, I utilize objects to organize react components based on a shared prop (e.g _type). My goal is to automatically determine the correct com ...

Here are some steps to turn off browser autocomplete for a p-inputMask field

I need help in disabling the browser autocomplete on a field that uses p-inputMask. I have tried using autocomplete="nope" on other fields and it works perfectly, but for this specific case, it doesn't seem to work. Can anyone point out what I might b ...

Create the Angular 6 service within the directory "e2e/app"

After upgrading my Angular 4 to 6, I attempted the following command: ng generate service security/security However, the service was generated under the "e2e/app" folder instead of the expected "src/app" location. Below is an excerpt from my angular.json ...

What is the method for adding a document within an array that is nested within another document?

Apologies if the title seems complex... I struggled to find a better way to describe it. The scenario I am dealing with fits the following Schemes: Collection1: const mongoose = require('mongoose'); const itemSchema = mongoose.Schema({ _id: ...

Service consumption across various components is failing to function as intended

I'm working on implementing a side navigation bar and content div. The goal is to display the innerText of the selected navigation item in the content div whenever an element in the side nav is clicked. Below is my current code setup: sidenav.compone ...

Simplify a function by lowering its cyclomatic complexity

This particular function is designed to determine whether a specific cell on a scrabble board qualifies as a double letter bonus spot. With a cyclomatic complexity of 23, it exceeds the recommended threshold of 20. Despite this, I am unsure of an alterna ...

Reviewing for the presence of "Undefined" in the conditional statement within Transpiled Javascript code for

While perusing through some transpiled Angular code, I came across this snippet: var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { I'm puzzled by the usage of undefined in this context. Can an ...

Why is Zod making every single one of my schema fields optional?

I am currently incorporating Zod into my Express, TypeScript, and Mongoose API project. However, I am facing type conflicts when attempting to validate user input against the user schema: Argument of type '{ firstName?: string; lastName?: string; pa ...

Angular2 data binding does not update when properties change

I'm struggling to establish the connection between the fields and the component in order for them to update when the properties change in OnDataUpdate(). The field "OtherValue" successfully binds two ways with the input field, and the "Name" field di ...