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

@studiohyperdrive/ngx-store

v18.0.3

Published

A set of utils and services to plug into a Redux/NGRX Angular workflow.

Downloads

224

Readme

Angular Tools: NgxStore (@studiohyperdrive/ngx-store)

NGX-store aims to reduce the boilerplate that comes with @ngrx, @ngrx/effects and @ngrx/entity; whilst still including the benefits of said packages.

Each of the provided utils can be used individually, but we strongly advice to use all three utils as a coherent unit to get the best developers experience.

Installation

Install the package first:

npm install @studiohyperdrive/ngx-store

Versioning and build information

This package will follow a semver-like format, major.minor.patch, in which:

  • major: Follows the Angular major version
  • minor: Introduces new features and (potential) breaking changes
  • patch: Introduces bugfixes and minor non-breaking changes

For more information about the build process, authors, contributions and issues, we refer to the ngx-tools repository.

Concept

As mentioned before, ngx-store works in tandem with @ngrx. The package aims to optimize the workflow for (complex) projects that currently already use this redux implementation.

With this in mind, our goal is to make this package as flexible as possible. We are aware that existing projects often already have their own approach to their store setup, and therefor we try to provide solutions that can be used when needed. Whilst we strongly suggest using the entire system for the optimal approach, several of our utils are entirely optional. The use of handleEffect, dispatchDataToStore and the StoreService is never mandatory and all utils are opt-in at any stage.

Because of this approach, our implementation has to take into account these constraints and will therefore deviate from the standard redux implementation.

Implementation

Store utils

createStoreAssets

In order to reduce the boilerplate of actions, reducers and selectors; and in the effort of creating a coherent store setup, NGX-Store uses the createStoreAssets util to setup a slice of the store.

A store slice can consists of multiple sub-slices, each one representing data we wish to save in the store. This data can either be in the form of an array, for which we use EntityStoreAssets; and other primitives like strings, numbers, objects, records, etc. which we save as BaseStoreAssets.

When using the createStoreAssets, we first define a type or interface which will represent the store slice we wish to create. This interface will be used by the util internally and is therefor required. An example of this type would be:

    type ExampleStoreAssets = {
	    channel: BaseStoreAssets<DataType>;
	    videos: EntityStoreAssets<DataType>;
    };

Once we have this type defined, we can use it in the createStoreAssets util and assign the correct generators to each sub-slice. These generators will provide a series of default actions, reducers and selectors which you can use throughout your application.

When using the createBaseStoreAssets generator, we are presented with the following actions and corresponding reducers: | Action | | |--|--| | set| Sets the provided data in the store | | loading| Sets the loading state of provided data in the store | | error| Sets the error state of the provided data in the store | | clear| Clears the earlier set data from the store | | effects.set| Dispatches a trigger for a set effect |

On top of that, we get a series of default selectors corresponding with the aforementioned actions. | Selector | | |--|--| | select| Selects the provided data from the store | | selectLoading| Selects the loading state of provided data from the store | | selectError| Selects the error state of the provided data from the store | | selectErrorMessage| Selects the provided error data of the provided data from the store |

When using createEntityStoreAssets, we use @ngrx/entity as a base to save data in array form. We strongly suggest using EntityStoreAssets for arrays, as @ngrx/entity has several optimized methods to handle arrays.

Using createEntityStoreAssets will provide you with the following actions and corresponding reducers. | Action | | |--|--| | set| Sets the provided data in the store | | add| Adds the provided data to the existing data in the store | | update| Updates the provided data in the existing data in the store | | delete| Deletes the provided data from the existing data in the store | | loading| Sets the loading state of provided data in the store | | error| Sets the error state of the provided data in the store | | clear| Clears the earlier set data from the store | | effects.set| Dispatches a trigger for a set effect | | effects.add| Dispatches a trigger for an add effect | | effects.delete| Dispatches a trigger for a delete effect | | effects.update| Dispatches a trigger for an update effect |

On top of the provided actions and reducers, the util also provides the following selectors.

| Selector | | | ------------------ | ------------------------------------------------------------------- | | selectAll | Selects the provided data from the store | | selectLoading | Selects the loading state of provided data from the store | | selectError | Selects the error state of the provided data from the store | | selectErrorMessage | Selects the provided error data of the provided data from the store |

Effects

Actions

To support @ngrx-effects the generator automatically generates a series of actions that can be used to handle effects. These can be found under the effects property, and match the set, add, update and delete actions.

In order to define the type of the payload of the effect actions, we can pass a secondary type to both the BaseStoreAssets and EntityStoreAssets (and their respective generators).

A short example can be found here

interface UserStore extends StoreFlowAssets {
	users: EntityStoreAssets<User, { set: void; add: string }>;
	paging: BaseStoreAssets<string>;
}

export const { actions, reducers, selectors } = createStoreAssets<UserStore>('users', [
	{
		subSlice: 'users',
		generator: createEntityAdapterStoreAssets<User, { set: void; add: string }>,
	},
	{
		subSlice: 'paging',
		generator: createBaseStoreAssets<string>,
	},
]);
handleEffect

As additional support to effects, the package provides a handleEffect operator which will automatically take care of any of the provided actions. By defining which sub-slice we wish to use, the action we wish to handle and the corresponding data source, the handleEffect operator will perform all the necessary actions required.

A short example can be found here

	public fetchUsers$ = createEffect(() => {
		return this.actions$.pipe(
			handleEffect<User[]>(actions.users, 'set', this.userService.fetchUsers)
		);
	});

dispatchDataToSTore

An additional util that works in tandem with the aforementioned store assets is the dispatchDataToStore util. Using the assets, the util will automatically handle the loading and error state of the provided data.

A short example can be found here

    public  getChannel():  Observable<DataType> {
	    return  dispatchDataToStore(
		    actions.channel,
		    this.httpClient.get<DataType>('test'),
		    this.store
		    );
    }

The util returns an Observable for easy further chaining throughout the application.

StoreService

Just as the aforementioned dispatchDataToStore util, the StoreService abstraction works best in tandem with the createStoreAssets util.

This abstraction provides easy access to the selectors that were generated by the assets. These are aimed to reduce the boilerplate and create a coherent store setup throughout your entire application. By passing the selector object to the methods, the abstraction will automatically handle the selection.

| method | | | --------------------------- | ------------------------------------------------------------------- | | selectFromStore | Selects the provided data from the store | | selectLoadingFromStore | Selects the loading state of provided data from the store | | selectErrorFromStore | Selects the error state of the provided data from the store | | selectErrorMessageFromStore | Selects the provided error data of the provided data from the store |

On top of that, the StoreService can automatically generate observables for each sub-slice in the provided store. By providing the store interface to the StoreService via generics and passing the generated selectors to the constructor, a state object is generated including all the necessary observables.

A short example would be:

interface StoreState extends StoreFlowAssets {
	isCompleted: BaseStoreAssets<boolean>;
}

export const { actions, reducers, selectors } = createStoreAssets<StoreState>('state', [
	{
		subSlice: 'isCompleted',
		generator: createBaseStoreAssets<boolean>,
	},
]);

@Injectable()
export class StoreStateService extends StoreService<StoreState> {
	constructor(protected readonly store: Store) {
		super(store, selectors);
	}
}

const storeStateService = new StoreStateService(store);

const state = storeStateService.state;
// Contains: isCompleted$, isCompletedLoading$, isCompletedError$ and isCompletedErrorMessage$