I need to store different types of controls (Buttons, Inputs, ...) in a Statemanagement. I would like to use the ngrx SignalStore with Entity Management for that.
Is it possible to define the Type of the Entities dynamic? I have the following type defintions:
export interface Control {
id: number;
visible: boolean;
}
export interface ButtonControl extends Control {
displayText: string;
}
export interface InputControl extends Control {
placeholder: string;
}
When instantiating the signalStore I can't pass a generic Type:
export const ControlsStore = signalStore(withEntities<Control>());
Using a function with an generic type I wouldn't have a singleton.
Any ideas on this?
If you want all the types to be stored on the same state, you can merge the types using typescript &
and make the derived types to be not mandatory using ?:
, by doing this you can use multiple types on 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',
})
);
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);
If you want to preserve the types, you must have a separate collection for each of the types, with a separate unique collection name!
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',
})
);
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);