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);
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);