Utilizing Dynamic Types within the ngrx SignalStore and Entities

In my quest to store a variety of controls (such as buttons, inputs, etc.) in a State management system, I am considering utilizing the ngrx SignalStore with Entity Management.

My dilemma lies in whether it is possible to dynamically define the type of entities. Below are the type definitions I have:

export interface Control {
  id: number;
  visible: boolean;
}

export interface ButtonControl extends Control {
  displayText: string;
}

export interface InputControl extends Control {
  placeholder: string;
}

Unfortunately, when setting up the signalStore, passing a generic Type seems impossible:

export const ControlsStore = signalStore(withEntities<Control>());

If I were to use a function with a generic type, having a singleton would be challenging.

Any thoughts or suggestions on how to approach this?

Answer №1

To store all types in a single state and make derived types optional, you can merge them using typescript's & operator and use ?:. This allows for multiple types to be used in the same state.

export interface Control {
  id: number;
  visible: boolean;
}

export interface ButtonControl extends Control {
  displayText?: string;
}

export interface InputControl extends Control {
  placeholder?: string;
}
export type ControlType = Control & ButtonControl & InputControl;

const TodoStore = signalStore(
  withState({ ids: [] }), // ids property already exists
  withEntities({
    entity: type<ControlType>(),
    collection: 'todo',
  })
);

FULL CODE:

import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { signalStore, withState, type, patchState } from '@ngrx/signals';
import { addEntities, withEntities } from '@ngrx/signals/entities';

export interface Control {
  id: number;
  visible: boolean;
}

export interface ButtonControl extends Control {
  displayText?: string;
}

export interface InputControl extends Control {
  placeholder?: string;
}
export type ControlType = Control & ButtonControl & InputControl;

const TodoStore = signalStore(
  withState({ ids: [] }), // ids property already exists
  withEntities({
    entity: type<ControlType>(),
    collection: 'todo',
  })
);

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <ul>
      @for (todo of todoStore.todoEntities(); track todo.id) {
        <li>{{ todo.id }}</li>
      }
    </ul>
  `,
  providers: [TodoStore],
})
export class App {
  todoStore = inject(TodoStore);

  ngOnInit() {
    patchState(
      this.todoStore,
      addEntities(
        [
          { id: 1, visible: true },
          { id: 2, visible: false, displayText: 'asdf' },
        ],
        { collection: 'todo' }
      )
    );
  }
}

bootstrapApplication(App);

Stackblitz Demo


If you wish to maintain type integrity, it is recommended to have separate collections for each type with unique names.

export interface Control {
  id: number;
  visible: boolean;
}

export interface ButtonControl extends Control {
  displayText: string;
}

export interface InputControl extends Control {
  placeholder: string;
}

const TodoStore = signalStore(
  withState({ ids: [] }), // ids property already exists
  withEntities({
    entity: type<Control>(),
    collection: 'control',
  }),
  withEntities({
    entity: type<ButtonControl>(),
    collection: 'button',
  }),
  withEntities({
    entity: type<InputControl>(),
    collection: 'input',
  })
);

FULL CODE:

import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { signalStore, withState, type, patchState } from '@ngrx/signals';
import { addEntities, withEntities } from '@ngrx/signals/entities';

export interface Control {
  id: number;
  visible: boolean;
}

export interface ButtonControl extends Control {
  displayText: string;
}

export interface InputControl extends Control {
  placeholder: string;
}

const TodoStore = signalStore(
  withState({ ids: [] }), // ids property already exists
  withEntities({
    entity: type<Control>(),
    collection: 'control',
  }),
  withEntities({
    entity: type<ButtonControl>(),
    collection: 'button',
  }),
  withEntities({
    entity: type<InputControl>(),
    collection: 'input',
  })
);

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <ul>
      @for (todo of todoStore.controlEntities(); track todo.id) {
        <li>{{ todo.id }}</li>
      }
    </ul>
    <ul>
      @for (todo of todoStore.inputEntities(); track todo.id) {
        <li>{{ todo.placeholder }}</li>
      }
    </ul>
  `,
  providers: [TodoStore],
})
export class App {
  todoStore = inject(TodoStore);

  ngOnInit() {
    patchState(
      this.todoStore,
      addEntities(
        [
          { id: 1, visible: true },
          { id: 2, visible: false },
        ],
        { collection: 'control' }
      )
    );
    patchState(
      this.todoStore,
      addEntities(
        [
          { id: 1, visible: true, placeholder: 'asdf' },
          { id: 2, visible: false, placeholder: 'asdf2' },
        ],
        { collection: 'input' }
      )
    );
  }
}

bootstrapApplication(App);

Stackblitz Demo

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 are the steps to creating a service that returns Observables?

I've been working on a project that utilizes Google Maps Places Autocomplete API to fetch a list of objects. The service is simple; I'm using the Maps JavaScript API to pass a string and receive a list of matches in return. However, the challeng ...

Having trouble with installing angular-cli globally using 'npm install -g angular-cli

Update: After successfully installing "npm install -g angular-cli," I encountered a new error message stating: "You cannot use the new command inside an angular-cli project" when trying to create a new project using "ng new ang2pro." C:\Users\a ...

Retrieve all items that match the ids in the array from the database

I'm having trouble receiving a list of items that match with my array of ids. Here's a snippet from the Angular component code: this.orderService.getSpecyficOrders(ids) .subscribe(orders => { ... Where ids is an array of [{_id : ID }, ...

Creating a new Angular app with package name is causing an error

Having trouble creating a new app using angular cli, as there seems to be an error with a package. Any guidance on how to resolve this? Summary: npm ERR! Unexpected end of JSON input while parsing near '...7w\nr0sNcY3SWENyNwkKN' Log: ...

The Heart of the Publisher-Subscriber Design Paradigm

After reading various online articles on the Publisher-Subscriber pattern, I have found that many include unnecessary domain-specific components or unreliable information inconsistent with OOP standards. I am seeking the most basic and abstract explanatio ...

What is the best way to handle waiting for an HTTP request to complete from a separate component?

https://i.sstatic.net/q4XYB.png An issue arises when calling the GetData function from a component's controller too early. I would like it to wait for the identification process to complete before triggering. During page loading, there is a server c ...

Updating an Angular 2 project for the MEAN Stack development platform

A few weeks back, I embarked on an Angular2 project by following the "Tour of Heroes" tutorial. As I progressed, my project grew in complexity with routers, rest services, and hundreds of lines of code. Now, as I look to transition my project to the MEAN ...

Retrieve all articles from a user using the TypeORM - findAll function

Is there a way to retrieve all articles of a specific user using the TypeORM package? In Sequelize, I have the following function: async findAllByUser(userUuid: string, findOptions: object): Promise<Article[]> { return await Article.findAll< ...

Designing a personalized mat-icon using the Github SVG

Trying to create a unique custom SVG mat-icon by loading the SVG directly from Github. My initial attempt using DomSanitizer was documented in this article, and it successfully loaded the SVG via HttpClient. Now, I am attempting to achieve this directly t ...

What is the process for linking my component to my socket.io server?

I am facing a challenge in setting up a socket.io server to facilitate communication between two components: a command interface for sending data, and an overlay component for receiving it. Below is the code snippet: interface.component.html : <input ...

What could be causing my date variable to reset unexpectedly within my map function?

Currently, I'm utilizing a tutorial to create a custom JavaScript calendar and integrating it into a React project You can find the functional JavaScript version in this jsfiddle import { useState, useRef, useMemo } from 'react' import type ...

Error in parsing: An unexpected symbol appeared, an identifier or keyword was expected at the end of the expression

Whenever I attempt to display data from an API, an error always pops up. My goal is to showcase each piece of data individually. To help you analyze the issue, I've included the URL of the API below: civilizationes.component.ts import { Component, O ...

Angular 4 HTTP Requests Failing to Retrieve JSON Data

I am currently working with the following method in my Typescript: allPowerPlants(onlyActive: boolean = false, page: number = 1): PowerPlant[] { const params: string = [ `onlyActive=${onlyActive}`, `page=${page}` ].join('&&apo ...

How can one display distinct error messages depending on the date range in Angular 7 and HTML?

Validation is needed for move-in date in relation to purchase and effective dates. The rule states that the move-in date must fall within the range of the purchase date and the policy effective date. To implement this, I added the following code to my Type ...

Having trouble getting POST requests to work in Angular 8 for unknown reasons

Hey there! I'm currently experimenting with using POST requests to an API in Angular for the first time, but unfortunately it's not working as expected. I've checked out some other examples of code and everything seems fine. Here is a snipp ...

Secure method of utilizing key remapped combined type of functions

Imagine having a union type called Action, which is discriminated on a single field @type, defined as follows: interface Sum { '@type': 'sum' a: number b: number } interface Square { '@type': 'square&apos ...

Deploying code from an S3 bucket to CodeCommit using AWS CDK

During the implementation of my CDK Stack, I encountered an issue when creating a Bucket, uploading files to it, and then creating a CodeCommit repository to store those files. Everything was going smoothly until I tried to create a new codecommit.CfnRepos ...

The specified property is not found within type T

Struggling to make a function more generalized here. The error message stating [E] Property 'displayName' does not exist on type 'T[{ [k in keyof T]: T[k] extends Base ? k : never; }[keyof T]]' has got me puzzled. interface Base { id: ...

Angular app not receiving a response from REST API call to JBoss - simply stuck in a 'stalled' state

I am currently facing a challenge with my Angular application interacting with an API hosted on a local standalone JBoss server. Suddenly, all API calls are getting stuck without any error messages. This issue started a week ago and has caused a complete ...

Oops! A mistake was made by passing an incorrect argument to a color function. Make sure to provide a string representation of a color as the argument next time

Encountering an issue with a button react component utilizing the opacify function from the Polished Library The styling is done using styled-components along with a theme passed through ThemeProvider. Upon testing the code, an error is thrown. Also, the ...