sds-signals
v2.1.0
Published
[# SDS Signal -- Reactive Action/Selector Framework for Angular
Downloads
2
Readme
[# SDS Signal -- Reactive Action/Selector Framework for Angular
A lightweight signal-based state & event system for Angular.
Works seamlessly in both Standalone and NgModule-based applications.
No boilerplate. No global store. Just@Action()+@Selector()+@Message()+@Error().
Features
- Works with Standalone Components and Modules
- Simple @Action() / @Selector() / @Message() / @Error() decorators
- Built on Angular's Signal API
- Global and Feature-level scopes
- Automatic cleanup (no memory leaks)
- No 3rd-party dependencies
Installation
Install via npm:
npm install sds-signalOr if using a local library:
npm install file:../dist/sds-signalSetup
Standalone Applications
import { bootstrapApplication } from '@angular/platform-browser';
import { provideSdsSignal } from 'sds-signal';
import { AppComponent } from './app/app.component';
import { JobActions } from './app/actions/job.actions';
bootstrapApplication(AppComponent, {
providers: [
provideSdsSignal([JobActions]), // register global actions
],
});Module-based Applications
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SdsSignalModule } from 'sds-signal';
import { AppComponent } from './app.component';
import { JobActions } from './actions/job.actions';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
SdsSignalModule.forRoot([JobActions]), // global registration
],
bootstrap: [AppComponent],
})
export class AppModule {}Defining Actions
import { Injectable } from '@angular/core';
import { Action, SdsSignalService } from 'sds-signal';
import { JobSelector, LegService } from '@sdsSignals';
import { catchError, of, tap } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class JobActions {
constructor(private sdsSignal: SdsSignalService, private defaultService: LegService) {}
@Action(JobSelector.List)
updateJob({ id }: JobSelector.List) {
return this.defaultService.fetch(id).pipe(
tap((result: any) => {
if (result.status === 1) {
return result;
} else {
throw new Error(result.error);
}
}),
catchError((error) => {
this.sdsSignal.onError(JobSelector.List, error?.error?.message);
return of('');
})
);
}
}Defining Action Classes / Namespaces
import { ServicesResponseInterface } from '@sdsSignals';
export namespace JobSelector {
export class Insert {
static readonly type = '[Job] Insert';
constructor(public payload: ServicesResponseInterface) {}
}
export class List {
static readonly type = '[Job] List';
constructor(public id: string) {}
}
export class Delete {
static readonly type = '[Job] Delete';
constructor(public payload: ServicesResponseInterface) {}
}
export class Error {
static readonly type = '[Job] Error';
}
}Using Selectors
import { Component, OnInit } from '@angular/core';
import { Selector, Message, SdsSignalService } from 'sds-signal';
import { JobSelector } from '@sdsSignals';
@Component({
selector: 'app-job-list',
standalone: true,
template: `
<h3>Jobs</h3>
<button (click)="reload()">Reload</button>
<ul>
<li *ngFor="let job of jobs">{{ job.name }}</li>
</ul>
`,
})
export class JobListComponent implements OnInit {
jobs: any[] = [];
constructor(private sdsSignal: SdsSignalService) {}
ngOnInit() {
this.sdsSignal.dispatch(new JobSelector.List({ id: '123' }));
}
@Dispatch(JobSelector.List)
dispatch() {
this.loader = true;
this.selection.clear();
return {
params: this.pageParams.paramString,
filters: this.activeFilters
}
}
@Selector(JobSelector.List)
onJobsList(data: any) {
console.log('Jobs List:', data);
}
@Message(JobSelector)
meg(msg: { messageType: 'success' | 'error'; message: string }) {
console.log('Message received:', msg);
}
@Message([JobSelector.List, JobSelector.Insert])
specificMsg(msg: { messageType: 'success' | 'error'; message: string }) {
console.log('Specific messages:', msg);
}
@Error(JobSelector)
errorMessage(error: any) {
console.log('Error:', error);
}
@Error([JobSelector.List, JobSelector.Insert])
errorMessage2(error: any) {
console.log('Error:', error);
}
}Dispatching & Notifications
Method Description
dispatch(new className(), ...args) Run an action and notify all selectors
notify(className, value) Push data directly without running an
action
onError(className, message) Send error message
onSuccess(className, message) Send success message
Example:
const sds = inject(SdsSignalService);
// Run async action
await sds.dispatch(new JobSelector.Insert({ payload: {} }));
// Push a value directly
sds.notify(JobSelector.List, [{ name: 'Job1' }]);
// Trigger messages manually
sds.onSuccess(JobSelector.List, 'Jobs loaded successfully');
sds.onError(JobSelector.Delete, 'Failed to delete job');Feature Modules / Lazy Routes
Lazy Modules
@NgModule({
imports: [SdsSignalModule.forFeature([FeatureActions])],
})
export class FeatureModule {}Standalone Lazy Routes
{
path: 'feature',
loadComponent: () =>
import('./feature.component').then((c) => c.FeatureComponent),
providers: [
provideSdsFeature([FeatureActions]),
],
}Lifecycle Handling
- Selectors are auto-registered on
ngOnInit - Selectors auto-unregister on
ngOnDestroy - No manual subscription cleanup required
Decorator & Method Reference Table
| Feature | Decorator / Method | Applies To | Description | Example Usage |
|--------|---------------------|------------|-------------|----------------|
| Action | @Action(ActionClass) | Action Service (Injectable) | Defines a reactive command. Executes your async logic (HTTP, business logic, etc.). | @Action(JobSelector.List) |
| Selector | @Selector(ActionClass \| ActionClasses[]) | Component | Listens to data updates pushed by an Action or manual notify. Auto-unsubscribes. | @Selector(JobSelector.List) |
| Message | @Message(Namespace \| ActionClass \| ActionClasses[]) | Component | Listens to success messages triggered via onSuccess(). | @Message(JobSelector) |
| Error | @Error(Namespace \| ActionClass \| ActionClasses[]) | Component | Listens to errors triggered via onError(). Supports wildcard error handler. | @Error(JobSelector) |
| Dispatch | @Dispatch(ActionClass) | Component | Auto-creates & dispatches an action from method’s returned object. Constructor-aware. | @Dispatch(JobSelector.List) |
| Manual Dispatch | sds.dispatch(new ActionClass(...)) | Anywhere | Runs an Action manually without the decorator. | sds.dispatch(new JobSelector.List(params)) |
| Notify | sds.notify(ActionClass, value) | Anywhere | Pushes data directly to selectors without executing an Action. | sds.notify(JobSelector.List, data) |
| Success Message | sds.onSuccess(ActionClass, msg) | Anywhere | Triggers a success message received by @Message. | sds.onSuccess(JobSelector.Insert, 'Inserted!') |
| Error Message | sds.onError(ActionClass, msg) | Anywhere | Triggers an error message received by @Error. | sds.onError(JobSelector.List, 'Load failed') |
| Global Setup | provideSdsSignal([Actions]) | Root | Registers actions globally. | provideSdsSignal([JobActions]) |
| Feature Setup | provideSdsFeature([Actions]) | Lazy/Feature Routes | Registers feature-scoped actions. | provideSdsFeature([FeatureActions]) |
Quick Reference
Concept Decorator / Function Purpose
Action @Action('key') Defines a
reactive command
Selector @Selector('key') Listens to
signal updates
Message @Message(namespace \| actions[]) Listens to
success messages
Error @Error(namespace \| actions[]) Listens to error
messages
Dispatch @Dispatch() Simplified action caller
Dispatch sds.dispatch(new className(), value?) Runs an action
Notify sds.notify('key', value) Manually trigger
selectors
onSuccess sds.onSuccess(className, message) Trigger success
message
onError sds.onError(className, message) Trigger error
message
Global provideSdsSignal() / forRoot() Root setup
Setup
Feature provideSdsFeature() / forFeature() Feature or lazy
Setup scope
Project Structure Example
src/
├── actions/
│ └── job.actions.ts
├── components/
│ └── job-list.component.ts
├── app.component.ts
└── main.tsAction → Selector → Message Flow Diagram
+-------------+ dispatch +-------------+ notify/message +-------------+
| @Action() | ------------------> | SdsSignal | --------------------------> | @Selector() |
| (method) | | Service | | / @Message()|
+-------------+ +-------------+ | / @Error() |
+-------------+License
MIT © 2025 --- SDS Signal Authors ]()
