``Engulfed in a sea of NgRx ViewModel

Apologies if this is unclear, there might be a fundamental aspect that I am overlooking, but here is my current issue:

  1. I am fetching a list of items from the backend, similar to:

    interface Item {
      id: number;
      userId: number;
      categoryId: number;
    }
    
  2. Additionally, I have lists of users and categories stored in my application:

    interface User {
      id: number;
      name: string;
    }
    
    interface Category {
      id: number;
      name: string;
    }
    
  3. I aim to create an ItemVM view model by utilizing these three classes, which will hold the processed data:

    interface ItemVM {
      id: number;
      userName: string;
      categoryName: string;
    }
    

My understanding suggests that I should implement a selector like this:

// map userId and categoryId to user.name and category.name
export const selectItemViewModel = createSelector(

  // retrieve users, categories, and items
  selectUsers,
  selectCategories,
  selectItems,

  // map them accordingly
  (users, categories, items) => {
    return items.map(i => <ItemVM>{
      id: i.id,
      userName: users.find(u => u.id === i.userId).name,
      categoryName: categories.find(c => c.id === i.categoryId).name,
    });
  }

);

However, my confusion lies in the fact that since this selector is not observable, how can I ensure that users, categories, and items are already loaded when this function is invoked?

Answer №1

Selectors serve as pure functions (with memoization) of the central state, accessible through a web browser extension called Redux DevTools.

If you want to prioritize the loaded aspect, include it in the state for each entity.

This code snippet is based on version 15.2.1 of @ngrx/entity.

Sample State of Item Entity, Repeat for Each Entity


Actions

export const apiActions = createActionGroup({
  source: 'Item/API',
  events: {
    'Load Items': emptyProps(),
    'Load Items Success': props<{ items: Item[] }>(),
    'Load Items Failure': props<{ error: string }>()
  },
});

Reducer

export interface ItemsState extends EntityState<Item> {
  loaded: boolean;
}

export const adapter: EntityAdapter<Item> = createEntityAdapter<Item>();

const initialState: ItemsState = adapter.getInitialState({
  // define initial required properties
  loaded: false
});

const reducer = createReducer(
  initialState,
  ...
  on(apiActions.loadItemsSuccess, (state, { items }) =>
    adapter.setAll(items, { ...state, loaded: true })
  ),
  ...
);

export const itemsFeature = createFeature({
  name: 'items',
  reducer
});

// register using StoreModule.forFeature(itemsFeature)

Selectors

export const {
  // selector for each feature state property
  selectEntities,
  selectLoaded
} = itemsFeature;

Create a Multi-Feature Selector

import { selectLoaded as selectItemsLoaded } from '<path>/items.selectors.ts';
import { selectLoaded as selectUsersLoaded } from '<path>/users.selectors.ts';
import { selectLoaded as selectItemsLoaded } from '<path>/categories.selectors.ts';

export const selectItemVmLoaded = createSelector(
  selectItemsLoaded,
  selectUsersLoaded,
  selectItemsLoaded,
  (l1, l2, l3) => ({ l1 && l2 && l3 })
)

selectItemVmLoaded can be included in your selector to easily determine whether to fetch [] and await API responses.

You can expose this in a service for displaying spinner or skeleton components.

  itemVmLoaded$ = this.store.select(selectItemVmLoaded);

Answer №2

Utilizing a unique selector with distinctUntilChange() is crucial for optimal functionality. Ensure that it is initialized at bootstrap using the initial state of each reducer, and always initialize with an empty array rather than null.

Your approach is right on track, everything is running smoothly here.

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

Connecting parent and child entities using ngrx and ngrx/data - A comprehensive guide

I am currently developing an Angular application for a Travel Agency. On the hotel listing page, I need to display the Country and City of each hotel. The data for Country, City, and Hotel is being fetched from ngrx/data EntityService. Although my nested ...

Launching an Angular application in a universal environment on Azure's service

My angular app is configured with Angular Universal. After the build pipeline, I have a server folder that I understand is necessary. However, when I use my app without server-side rendering (SSR), I point the path in Azure App Settings to my browser folde ...

I'm struggling to include a link in my project card component - I've tried using both the Link tag and anchor tag, but so far, I haven't been successful in

I am having trouble getting the link tag to work properly in my UI. I have tried using both the link and anchor tags, but neither seems to be functioning as expected. Can someone please advise on how to fix this issue? https://i.sstatic.net/tAD7C.png I w ...

What role does the @Input statement in the HeroDetailComponent class serve in the Angular 2 Quickstart tutorial?

Looking at the multiple components part of the Angular 2 Quickstart tutorial, we see how a component is separated from the AppComponent to enhance reusability and testing convenience. You can try out the example live demo here. In this scenario, users ar ...

Utilize a personalized useFetch hook in React.js to transmit a POST request and obtain a response

I recently came across a great resource on this website that provided the logic for a useFetch hook. My goal is simple - I want to send a post request and then map the response into a specific type. While this seems like it should be straightforward, I&apo ...

When using TypeScript, my sorting function returns a value of 0 for all time values

I have been trying to sort this JSON data by date using the provided code, but it does not seem to work as expected. Below is a snippet of my JSON: { "StatusCode":0, "StatusMessage":"OK", "StatusDescription":[ { "id":"1", ...

Using Typescript to create a mapped type that allows for making all properties read-only, with the exception of

I encountered a problem where I didn't want to repeatedly rewrite multiple interfaces. My requirement is to have one interface with full writing capabilities, while also having a duplicate of that interface where all fields are set as read-only excep ...

What is the best way to declare a minimum and maximum date in HTML as the current date?

I have a question regarding setting the min/max date for a date input in my Angular 6 project. How can I ensure that only dates up to the current date are enabled? So far, I have attempted to initialize a new Date object in the ngOnInit function and set t ...

No solution was found for implementing Airbnb TypeScript in React.js (Next.js) using ESLint

screenshot I encountered an issue where I couldn't locate the Airbnb typescript option in React JS (Next JS) within ESLint. Prior to this, I had installed Storybook and mistakenly clicked "yes" when prompted to migrate ESLint to Storybook. ...

What is the importance of context in the subscription method of RxJS or Angular Observables?

In the given situations, I am providing a child Component with a property that is updated later through an RxJs Observable subscription. Angular fails to detect changes when not using an anonymous function or binding the this context. // Situation 1 // C ...

Typescript struggling to load the hefty json file

Currently, I am attempting to load a JSON file within my program. Here's the code snippet that I have used: seed.d.ts: declare module "*.json" { const value: any; export default value; } dataset.ts: import * as data from "./my.json" ...

``There seems to be an issue with Angular2 routing not functioning properly due to the presence of

Seeking assistance with an issue I'm facing. I am attempting to pass parameters in routing. However, when my parameter contains a slash symbol, Angular interprets the text after the slash as another parameter, causing my routing to fail. { path: &ap ...

"Optimizing Angular (v4+) for Peak Performance: Expert Strategies and

Currently delving into Angular JS, I've kicked off an Angular Project through the Angular CLI with the core version standing at 5.1.0. Seeking guidance on best practices when it comes to crafting a stellar UI. Your insights and tips on the matter wou ...

Unique Version: Some effective tips for utilizing a fork of type definition such as @types

Currently, I am utilizing Typescript 2.0 along with @types and the experience has been quite positive. Thanks to @types, we can easily leverage type definitions by simply installing the package via npm. Surprisingly, I have not delved into how it actually ...

Tips for showcasing information from an array in an object using Angular 2 or newer versions

My Information consists of 2 offices while another contains just one office "id": "1", "username": "sample", "groups": "Admin", "office": [ { "officeid": "2", "officename": "sky" }, { "officeid": "3", "off ...

Unraveling Angular2 Dependency Injection: Decoding the mysterious syntax seen preceding certain injected package names (e.g. 'providers = [...SomeProvider]')

As I delve into learning Angular2, I have stumbled upon a new syntax for injecting providers. providers : [SomeProvider], Interestingly, some packages are now using a "..." before the provider name like this: providers : [...SomeProvider], This got me ...

Obtain the accurate property value from Redux

I've been working on a login page using React Native, Redux, and Thunk as middleware. I've encountered several issues related to fetching values from my actions when certain events occur. The login page makes a call to a NodeJS server. Upon click ...

Encountering a problem: Unable to locate a supporting object '[object Object]' of type 'object' when attempting to populate a list of objects

Struggling to populate the options with server data, I tried simplifying the logic yet encountered the same error. Here's the HTML snippet for the options: <div class="col-md-4"> <mat-form-field class="example-full-width" *ngIf="!isAdmin; e ...

A guide to connecting an object's value with HTML using Angular

I'm encountering an issue while attempting to display values from a single object in HTML. { "id": "221", "user_id": "455", "membership_id": "3", "is_cemetery": "0" ...

Troubleshooting: Issue with reloading in MERN setup on Heroku

I successfully deployed my MERN stack application on Heroku using the following code: import express from "express"; import path from "path"; const app = express(); const port = process.env.PORT || 5000; app.get("/health-check&qu ...