New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@mmuscat/angular-actions

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mmuscat/angular-actions

A tiny (1kb) state management library for Angular Composition API.

latest
Source
npmnpm
Version
0.1200.0-next.4
Version published
Weekly downloads
4
Maintainers
1
Weekly downloads
 
Created
Source

Angular Actions

A tiny (1kb) state management library for Angular Composition API.

Quick Start

Install via NPM

npm install @mmuscat/angular-actions

Install via Yarn

yarn add @mmuscat/angular-actions

Create a store

Define initial state factory. Supports dependency injection.

export function getInitialState() {
   return {
      count: 0,
   }
}

Actions

Create actions, with or without props.

const Increment = new Action("Increment")
const IncrementBy = new Action("Increment", props<{ by: number }>())

Reducers

Create reducers. A list of action reducers can be added for producing the next state. Supports dependency injection.

const Count = new Reducer<number>("count", (reducer) =>
   reducer.add(Increment, (state, action) => state + 1),
)

Effects

Create effects. Effects are factory functions that should return an Observable. Supports dependency injection. Effects will receive the store injector as an argument.

function logCount(store: Store) {
   const count = store(Count)
   return count.pipe(tap(console.log))
}

function autoIncrement(store: Store) {
   const increment = store(Increment)
   return interval(1000).pipe(tap(increment))
}

Error Recovery

Effects will stop running by default when an error occurs. To prevent this from happening, handle the error using catchError or another retry strategy. If you just want errors to be reported while keeping an effect running, return a materialized stream from an error-producing inner observable.

function effectWithErrors(store: Store) {
   const http = inject(HttpClient)
   const source = store(Count)
   const result = store(ResultAction)
   return source.pipe(
      switchMap((count) =>
         http.post("url", { count }).pipe(
            map(result),
            materialize(), // should be placed on an inner stream
         ),
      ),
   )
}

Module Store

Create a Store.

const AppStore = new Store("AppStore", {
   state: getInitialState,
   reducers: [Count],
   effects: [logCount, autoIncrement],
})

Provide the store to root module. Additional stores can be configured for each lazy loaded module. Effects will run immediately on bootstrap.

@NgModule({
   imports: [StoreModule.config(AppStore)],
})
export class AppModule {}

Component Store

Create a Store.

const MyStore = new Store("MyStore", {
   state: getInitialState,
   reducers: [Count],
   effects: [logCount, autoIncrement],
})

Provide and use the store in a component. Effects won't run until the store is injected.

function setup() {
   const store = inject(MyStore)
   const count = store(Count) // get value from store
   const increment = store(Increment) // get dispatcher from store

   return {
      count,
      increment,
   }
}

@Component({
   providers: [MyStore.Provider],
})
export class MyComponent extends ViewDef(setup) {}

Important: A note about dependency injection

You must use the store's injector to retrieve actions and values.

const count = inject(Count) // don't do this!
const store = inject(MyStore) // inject store first
const count = store(Count) // this will be the correct instance

API Reference

Action

Creates an injectable Emitter that will emit actions of a given kind, with or without data.

const Increment = new Action("Increment")
const SaveTodo = new Action("SaveTodo", props<Todo>())

Actions can be injected inside the setup function of a ViewDef or Service factory. This returns an Emitter that be used to dispatch or listen to events.

function setup() {
   const store = inject(MyStore)
   const increment = store(Increment)

   subscribe(increment, ({ kind }) => {
      console.log(kind) // "Increment"
   })

   setTimeout(increment, 1000)

   return {
      increment,
   }
}

@Component()
export class MyComponent extends ViewDef(setup) {}

Reducer

Creates an injectable Value that reduces actions to produce a new state. The state of the reducer is hydrated using the object key of the same name returned by getInitialState.

function getInitialState() {
   return {
      count: 0, // state key must match reducer name
   }
}

const Count = new Reducer(
   "count", // reducer name must match state key
   (reducer) => reducer.add(Increment, (state, action) => state + 1),
   // .add(OtherAction, (state, action) => etc)
)

You can also supply a list of actions to a single reducer.

const Increment = new Action("Increment", props<{ by: number }>())
const Add = new Action("Add", props<{ by: number }>())

const Count = new Reducer(count, (reducer) =>
   reducer.add([Increment, Add], (state, action) => state + action.by),
)

Reducers can be injected inside the setup function of a ViewDef or Service factory. This returns a Value that be used to get, set or observe state changes.

function setup() {
   const store = inject(MyStore)
   const count = store(Count)

   subscribe(() => {
      console.log(count()) // 0
   })

   return {
      count,
   }
}

@Component()
export class MyComponent extends ViewDef(setup) {}

props

Returns a typed function for producing data on an Action.

const Increment = new Action("Increment", props<{ by: number }>())

Which is equivalent to

const Increment = new Action("Increment", (data: { by: number }) => data)

Keywords

Angular

FAQs

Package last updated on 04 Sep 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts