npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@mmuscat/angular-actions

v0.1200.0-next.4

Published

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

Downloads

12

Readme

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)