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

ngrx-store-easy

v0.2.0

Published

This library provides a much easier way to write Angular applications that use ngrx/store to manage state.

Downloads

12

Readme

ngrx-store-easy

This library provides a much easier way to write Angular applications that use ngrx/store to manage state.

Benefits

  • No string constants are needed for action types.
  • Reducer functions that switch on action type are not needed.
  • Actions can be dispatched by providing just a type and payload rather than an action object.
  • Each action type is handled by a single reducer function that is registered by action type and is simple to write.
  • Simple actions that merely set a property value in the state (the most common kind) can be dispatched without writing reducer functions (see dispatchSet).
  • Actions that only add elements to the end of an array can be dispatched without writing reducer functions (see dispatchPush).
  • Actions that only remove elements from an array can be dispatched without writing reducer functions (see dispatchFilter).
  • Actions that only modify elements in an array can be dispatched without writing reducer functions (see dispatchMap).
  • All objects in the Redux state are automatically frozen to prevent accidental state modification.
  • Asynchronous actions are handled in a simple way without requiring middleware or thunks (coming soon).
  • The complexity of nested/combined reducers can be bypassed.
  • Redux state is automatically saved in sessionStorage (on every state change, but limited to once per second).
  • Redux state is automatically loaded from sessionStorage when the browser is refreshed to avoid losing state.
  • Integration with redux-devtools is automatically configured.

Example app

See https://github.com/mvolkmann/angular-form-demo.

Setup

In app.module.ts, add the following imports:

import {getDeclarations, getImports, StateService} from './state.service';
import {ReduxCheckboxesComponent} from './nse-checkboxes.component';
import {ReduxInputComponent} from './nse-input.component';
import {ReduxRadioButtonsComponent} from './nse-radio-buttons.component';

Add the following to the declarations array:

NseCheckboxesComponent,
NseInputComponent,
NseRadioButtonsComponent,
NseSelectComponent,
NseTextAreaComponent,

Add the following to the imports array:

...getImports(environment),

Add StateProvider to the providers array.

If custom reducers are needed, define them like the following example in any source files that are loaded before the action is dispatched:

import {addReducer} from 'ngrx-store-easy';

// Call addReducer once for each action type, giving it the
// function to be invoked when that action type is dispatched.
// These functions must return the new state
// and cannot modify the existing state.
addReducer('addToAge', (state, years) => {
  const {user} = state;
  return {
    ...state,
    user: {
      ...user,
      age: user.age + years
    }
  };
});

In components that need to dispatch actions, do something like the following:

import {dispatch, watch} from 'ngrx-store-easy';
...
    // This assumes that addReducer was called earlier to define
    // a reducer function for the action type "setFirstName".
    dispatch('setFirstName', firstName);

    // If the setFirstName action just sets a value in the state,
    // perhaps user.firstName, the following can be used instead.
    // There is no need to implement simple reducer functions.
    dispatchSet('user.firstName', '', value);

In components that need to render data from the state, do something like the following:

import {dispatch, watch} from 'ngrx-store-easy';
...
  constructor(private stateSvc: StateService) {
    // The second argument to watch is a map
    // from component properties to state paths
    // where path parts are separated by periods.
    // Those properties will be updated any time
    // the state at the corresponding path changes.
    stateSvc.watch(this, {colors: 'person.colors'});
  }

When the value for a prop comes from a top-level state property with the same name, the path can be an empty string, null, or undefined and watch will use the prop name as the path.

Form Elements Tied to State Paths

It is common to have input, select, and textarea elements with onChange handlers that get their value from event.target.value and dispatch an action where the value is the payload. An alternative is to use the provided nse-input, nse-select, and nse-textarea components as follows:

HTML input elements can be replaced by the nse-input component. For example,

<nse-input path="user.firstName"></nse-input>

The type property defaults to 'text', but can be set to any valid value including 'checkbox'.

The value used by the nse-input is the state value at the specified path. When the user changes the value, this component updates the value at that path in the state.

To perform additional processing of changes such as validation, supply an onChange prop that refers to a function (coming soon).

HTML textarea elements can be replaced by the nse-textarea component. For example,

<nse-textarea path="feedback.comment"></nse-textarea>

HTML select elements can be replaced by the nse-textarea component. For example,

<nse-select path="user.favoriteColor">
  <option>red</option>
  <option>green</option>
  <option>blue</option>
</nse-select>

If the option elements have a value attribute, that value will be used instead of the text inside the option.

For a set of radio buttons, use the nse-radio-buttons component. For example,

<nse-radio-buttons
  class="flavor"
  [list]="radioButtonList"
  path="favoriteFlavor"
></nse-radio-buttons>

where radioButtonList is a component property that is set as follows:

const radioButtonList = [
  {text: 'Chocolate', value: 'choc'},
  {text: 'Strawberry', value: 'straw'},
  {text: 'Vanilla', value: 'van'}
];

When a radio button is clicked the state property favoriteFlavor will be set the value of that radio button.

For a set of checkboxes, use the nse-checkboxes component. For example,

<nse-checkboxes class="colors" [list]="checkboxList"></nse-checkboxes>

where checkboxList is set as follows:

const checkboxList = [
  {text: 'Red', path: 'color.red'},
  {text: 'Green', path: 'color.green'},
  {text: 'Blue', path: 'color.blue'}
];

When a checkbox is clicked the boolean value at the corresponding path will be toggled between false and true.

Asynchronous Actions

If a function passed to addReducer returns a Promise and a matching action is dispatched, it will wait for that Promise to resolve and then update the state to the resolved value of the Promise.

Here's an example of such a reducer function:

addReducer('myAsyncThing', (state, payload) => {
  return new Promise(async (resolve, reject) => {
    try {
      // Data in payload would typically be used
      // in the following call to an asynchronous function.
      const result = await fetch('some-url');

      // Build the new state using the current state
      // obtained by calling getState() rather than
      // the state passed to the reducer function
      // because it may have changed
      // since the asynchronous activity began.
      const newState = {...getState(), someKey: result};

      resolve(newState);
    } catch (e) {
      reject(e);
    }
  });
});

That's everything to you need to know to use ngrx-store-easy. Code simply!

Reminders for maintainers

To release a new version:

  1. bump version in package.json
  2. check in and push all changes
  3. npm run package
  4. npm run publish