What is the best way to combine a Signal containing an array of Signals in Angular using the merge(/mergeAll) operator?

When working in the world of rxjs, you have the ability to combine multiple Observables using the merge operator. If you have an array of Observables, all you need to do is spread that array into the merge operator like this: merge(...arrayOfObservables). And if you're dealing with a 'higher-order' Observable (an Observable that emits other Observables), then you can utilize mergeAll.

Now, I'm trying to achieve something similar to mergeAll with Angular's new Signals. I have a Signal that contains an array of Signals,

all_the_states = signal([] as Signal<any>[])
. This Signal continuously gets updated over time using mutate. My goal is to create a simple Signal called latest_state that always holds the most recent value from any of the 'inner' Signals.

My initial thought was to go to "rxjs-land, merge, and return", which led me to try the following approach:

latest_state = toSignal(
    merge(...this.all_the_states().map((single) => toObservable(single))),
    { initialValue: null }
  )

However, whenever I add something to all_the_states, latest_state remains null.

Another attempt involved the following code snippet:

higher_order_observable: Observable<Observable<any>[]> =
  toObservable(this.selection_streams).pipe(
    map(signals => signals.map(signal => toObservable(signal)))
  )
single_observable: Observable<any> =
  this.higher_order_observable.pipe(mergeAll(), mergeAll())
latest_state = toSignal(this.single_observable, { initialValue: null })

This solution seems promising in terms of types, although I am uncertain about the double mergeAll. However, Angular throws an error at me saying

NG0203: toObservable() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with 'runInInjectionContext'
even before reaching the double mergeAll.

Answer №1

If you have a signal containing an array of signals - something along these lines?

const allSignals = signal([new signal(1), new signal(2)]);

In this scenario, you can easily utilize a computed function to map the individual signals to an array of their values. There's no need to bring in rxjs.

const latestValues = computed(() => allSignals().map(val => val());

Check out StackBlitz demo here

Answer №2

const updatedState = toSignal(
    merge(...this.collectAllStates().map((state) => toObservable(state))),
    { initialValue: null }
  )

An issue arises with this code when attempting to access the signal outside of the reactivity context:

...this.collectAllStates()

In this snippet, a Signal is being accessed without being in the proper reactivity context - no consumer is registered to be notified when a new value is received by the Signal.

Daniel Gimenez has already provided an effective solution for achieving your desired outcome. I simply wanted to clarify why the existing code may not function as expected.

To gain a better understanding of what constitutes a reactivity context, feel free to refer to my article.

Answer №3

Kudos to OZ_ for pointing me in the right direction! It turns out that my initial solution, which looked like this:

latest_state = toSignal(
    merge(...this.all_the_states().map((single) => toObservable(single))),
    { initialValue: null }
  )

wasn't far off base. The key was to incorporate the reading of all_the_states and the calls to toObservable within a context of "reactivity" or "injection." This involved wrapping them with Angular's runInInjectionContext, utilizing a local Injector, and adding an extra mergeAll.

So, here's the updated code snippet: Start by obtaining an Injector in your Angular Component or Service:

import { Injector } from '@angular/core';

class ComponentOrService {
  constructor(private injector: Injector) {
  }
}

Then, inside that Component/Service, follow these steps (broken down into multiple parts for clarity):

all_the_states_with_inner_observables =
  computed(() => runInInjectionContext(this.injector,
    () => this.all_the_states().map((single) => toObservable(single))
  ))
states_as_obs_of_obs = toObservable(computed(() =>
  merge(...this.all_the_states_with_inner_observables())
))
latest_state_as_obs = this.states_as_obs_of_obs.pipe(mergeAll())
latest_state = toSignal(this.latest_state_as_obs, { initialValue: null })

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

What is the significance of an equals sign being located within the angle brackets of a type parameter?

Within the type definitions for Bluebird promises, there is a catch function that has the following definition: catch<U = R>(onReject: ((error: any) => Resolvable<U>) | undefined | null): Bluebird<U | R>; The symbol "R" is derived fr ...

Adjust the scroll bar to move in the reverse direction

I'm trying to modify the behavior of an overlay div that moves when scrolling. Currently, it works as expected, but I want the scroll bar to move in the opposite direction. For example, when I scroll the content to the right, I want the scroll bar to ...

Why is my Angular promise unexpectedly landing in the error callback?

I am facing an issue with my Angular + Typescript client. I have developed a PHP API and need to send a post request to it. Upon receiving the request, the server fills the response body with the correct data (verified through server debugging). However, w ...

Error: Component fails to compile when imported via a module in TestBed

Struggling to write unit tests for a component that includes another component tag in its HTML. <bm-panel [title]="title" [panelType]="panelType" [icon]="icon" class="bm-assignment-form-panel"> <div *ngIf="isLoading" class="bm-loader"> ...

Is it possible to obtain the URL (including parameters) of the first navigation before proceeding with the navigation process?

My goal is to automatically navigate to a specific website when a certain condition for the URL is met. Consider the following scenario in the ngOnInit() method of app.component.ts: if (urlMatchesCondition()) { await this.router.navigateByUrl('sp ...

In JavaScript, the function will return a different object if the string in an array matches the

My task involves working with a simple array of string ids and objects. Upon initial load, I am matching these Ids with the objects and setting the checked property to true. const Ids = ['743156', '743157'] [ { "id&quo ...

Is the window.location.href of a component not inside a router-outlet updated after a router redirect through a guard?

The navigation component I have is positioned outside of the router. It utilizes a reusable icon component that depends on absolute URLs to point to SVG icons, retrieved through a getter within the component: public get absUrl(): string { return windo ...

Extending parent context in dependencies through OOP/Typescript as an alternative to using "extends"

Introducing a custom class called EventBus has been a game-changer for me. This class allows for easy attachment of on/off/once methods to any class that extends it, enabling the creation of an array of events that can be listened to. Currently, I find my ...

The issue of resolving NestJs ParseEnumPipe

I'm currently using the NestJs framework (which I absolutely adore) and I need to validate incoming data against an Enum in Typscript. Here's what I have: enum ProductAction { PURCHASE = 'PURCHASE', } @Patch('products/:uuid&apos ...

Is there a way to programmatically trigger a CodeAction from a VSCode extension?

Can I trigger another extension's code action programmatically from my VSCode extension? Specifically, I want to execute the resolveCodeAction of the code action provider before running it, similar to how VSCode handles Quick Fixes. Most resources su ...

Checking a String for a Specific Pattern in Angular 6 using Regular Expressions

urn:epc:id:sgln:345344423.1345.143 I need to verify if the string above, entered into a text box, matches the specified format consisting of (urn:epc:id:sgln) followed by numbers separated by two dots. ...

There appears to be an issue with the compilation of the TypeScript "import { myVar }" syntax in a Node/CommonJS/ES5 application

In my Node application, I have a configuration file that exports some settings as an object like this: // config.js export var config = { serverPort: 8080 } Within another module, I import these settings and try to access the serverPort property: // ...

Issue with OpenAI's Rate Limit 429 Restriction

I've been experimenting with this repository in order to implement semantic search for YouTube videos using OpenAI + Pinecone. However, I keep encountering a 429 error at the following step - "Run the command npx tsx src/bin/process-yt-playlist.ts to ...

What could be causing the inner array typescript to be inaccessible in an Angular 5 application?

Below are the JSON definitions that I am working with: export class Company { name: string; trips : Trip[] = []; } export class Trip{ id: number; name: string; } Within the component, there is a method that contains the ...

Using async await with Angular's http get

In my service component, I have the following code snippet that retrieves data from an API: async getIngredientsByProductSubCategory(productSubCategoryId: number) { const url = '/my-url'; let dataToReturn: any; await this.http.get(ur ...

A: TypeScript Error TS7006: Parameter implicitly has an 'any' type

TS7006: The parameter 'port' is implicitly assigned an 'any' type. constructor(port) { TS7006: The parameter 'message' is implicitly assigned an 'any' type. Emit(message) { I'm confused because all the other r ...

Steps for running a TypeScript project as a child process within a JavaScript project

I am facing an issue with integrating my Electron app, written mainly in JavaScript, with an Express server project built in TypeScript. When I attempt to create a child process of the TypeScript project within my electron.js file, I encounter TypeScript e ...

Using Angular 4 and Bootstrap to create a Modal Component

I am contemplating the idea of integrating a component into an application that can function both as a Bootstrap modal and as a regular child component within a page. In the example provided on the referenced link, a component being used in a modal requir ...

When I attempt to add a todo item by clicking, the Url value is displayed as "undefined"

I am facing an issue with my household app where, upon clicking the button to navigate to the addtodo page, the URL specific to the user's house is getting lost. This results in the todolist being stored as undefined on Firebase instead of under the c ...

rxjs subscriptions in Angular

When setting up a subscription in Angular, I am declaring it as follows: counterSubscription: Subscription However, an error is being thrown stating: Property 'counterSubscription' has no initializer and is not definitely assigned in the const ...