angulartypescriptrxjssignalsangular-signals

Is there a way to dynamically compute rxResource() or resource() in Angular (19)


Our use case: in a component, we compute queries for data that should be fetched based on some config. I tried creating rxResource() within a compute to get the data - but also loading status and errors for each query individually.

  private readonly dataQueries = computed<DataQuery[]>(() => {
    // compute queries from component state
  });

  private readonly dataResources = linkedSignal<
    DataQuery[],
    DataResource[]
  >({
    source: this.dataQueries,
    computation: (newQueries, previousResult) => {
      // iterate over all queries and
      // check if we have a resource already for a query,
      // if yes, reuse the rxResource for it,
      // otherwise create a new one
    }
  }).asReadonly();

  protected readonly dataResults = computed<DataResults[]>(() =>
    this.dataResources().map((r) => r.value())
  );

  protected readonly dataStatuses = computed<ResourceStatus[]>(() =>
    this.dataResources().map((r) => r.status())
  );

However, this approach has two main drawbacks:

  1. Most important: rxResource uses internally effect() when constructed, and this is not allowed during computation of a signal value (and for a good reason, I agree, as in most cases this is not what you want)
  2. It does not appear as good style to me to use a linkedSignal to cache previous results in order to avoid re-creating resources for queries that existed already (ngxtensions's extendedCompute is deprecated now and suggests to use a linkedSignal instead)

I could use a single rxResouce that holds the combined results - but I then loose the individual status and error state information.

The classical approach would be to use Observables, I guess, and add loading state and errors to the results before assembling them into the final array. But as the resource() is a very nice way to combine value, state, and errors, it would be nice, if I could use this for each query.

Do I miss something? Any suggestions?


Solution

  • You approach is fundamentally flawed as far as my understanding goes.

    The resource APIs in general are initializer APIs, which are defined on the class top level (Same level as properties) or in the constructor, the point I want to make is, Define the resource API only once and never place it inside a reactive context, because it itself is a reactive element.

    The resource API is a reactive entity, that is supposed to be defined once and should react to signal changes.


    It has no place inside a computed, because it can achieve reactivity by itself without relying on computed.

    Each time the signals inside the computed change, a new instance of resource is created (Absolutely no benefit but guaranteed memory leak).


    So please initialize your resource as the top most level of reactivity.


    Then we can use computed or linkedSignal to derive the subsequent states required by the component.


    Solution:

    Basically you need to make a normal http call and not use the resource API, since this does not feel like a good use case for the resource API.