Giter Club home page Giter Club logo

entity-state's Introduction


Easy CRUD actions for your ngxs state

Build Status NPM License

This package is an entity adapter and simplifies CRUD behaviour with just a few lines of setup per state class!

Setup

npm i @ngxs-labs/entity-state

You do not have import any module, just extend your state class, make a super call and you are good to go! The first super parameter is always the state class itself. The second parameter is the key to identify your entities with. The third is an implementation of an IdGenerator (see below).

Example state

export interface ToDo {
  title: string;
  description: string;
  done: boolean;
}

@State<EntityStateModel<ToDo>>({
  name: 'todo',
  defaults: defaultEntityState()
})
export class TodoState extends EntityState<ToDo> {
  constructor() {
    super(TodoState, 'title', IdStrategy.EntityIdGenerator);
  }
}

Example in the integration app!

Actions

There are ready to use Actions for entity-states. Just pass in the targeted state class as the first parameter and then your action's payload.

this.store.dispatch(new SetLoading(TodoState, this.loading));
this.store.dispatch(new UpdateActive(TodoState, { done: true }));

Example in the integration app

Action Short description
Add Adds a new entity and cannot replace existing entities
CreateOrReplace Gets the entity's ID and will replace entities with same id or else add a new one
Update Updates one or more entities by partial value or function
UpdateAll Update all entities by partial value or function
Remove Removes entities from the state
RemoveAll Removes all entities from the state
---------- ----------
SetActive Takes an ID and sets it as active
ClearActive Clears the active ID
UpdateActive Updates the currently active entity
RemoveActive Removes the active entity and clears the ID
---------- ----------
SetError Sets the error (Error instance or undefined)
SetLoading Sets the loading state (true or false)
Reset Resets the state to default
---------- ----------
GoToPage Goes to specified page, via index, stepwise or first/last
SetPageSize Sets the page size

Actions that change the entities will update the internal timestamp lastUpdated. You can use one of the existing selectors to see the age of your data.

Selectors

Use predefined Selectors just like you would normally!

@Select(TodoState.entities) toDos$: Observable<ToDo[]>;
@Select(TodoState.active) active$: Observable<ToDo>;

Example in the integration app

Selector Short description
entities All entities in an array
keys All entity keys in an array
entitiesMap All entities in a map
size Entity count
active the active entity
activeId the active ID
paginatedEntities Entities in an array defined by pagination values
nthEntity the nthEntity by insertion order
latestId the ID of the latest entity
latest the latest entity
loading the loading state
error the current error
lastUpdated the lastUpdated timestamp as Date
age difference between Date.now() and lastUpdated in ms

IdStrategy

There are 3 different strategies in the IdStrategy namespace available:

  • IncrementingIdGenerator -> uses auto-incremeting IDs based on present entities
  • UUIDGenerator -> generates UUIDs for new entities
  • EntityIdGenerator -> takes the id from the provided entity

The latter will cause errors if you try to add an entity with the same ID. The former two will always generate a new ID.

You can also implement your own strategy by extending IdGenerator and then provide it in the super call.

EntitySelector

The EntitySelector type is used in Actions such as Update or Remove.

export type EntitySelector<T> = string | string[] | ((entity: T) => boolean);
  • string -> one ID; selects one entity
  • string[] -> array of IDs; selects matching entities
  • (entity: T) => boolean -> predicate; selects entities that return true when applied to this function

entity-state's People

Contributors

eranshmil avatar janmalch avatar narhakobyan avatar renovate-bot avatar renovate[bot] avatar splincode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

entity-state's Issues

Error: Attempted to get members of a non-class: class Add - NGCC

There seems to be an issue with running ngcc postinstall

Angular version: 10.2.3

Ivy compiler enabled in tsconfig.json with:

"angularCompilerOptions": {
    "enableIvy": true
},

Error Reported:

Error: Error on worker #2: Error: Attempted to get members of a non-class: "class Add {
        /**
         * Generates an action that will add the given entities to the state.
         * The entities given by the payload will be added.
         * For certain ID strategies this might fail, if it provides an existing ID.
         * In all other cases it will overwrite the ID value in the entity with the calculated ID.
         * @param target The targeted state class
         * @param payload An entity or an array of entities to be added
         * @see CreateOrReplace#constructor
         */
        constructor(target, payload) {
            return generateActionObject(exports.EntityActionType.Add, target, payload);
        }
    }"
    at UmdReflectionHost.Esm2015ReflectionHost.getMembersOfClass (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\host\esm2015_host.js:178:23)
    at DelegatingReflectionHost.getMembersOfClass (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\host\delegating_host.js:103:34)     
    at C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\analysis\module_with_providers_analyzer.js:77:32
    at Map.forEach (<anonymous>)
    at ModuleWithProvidersAnalyzer.getModuleWithProvidersFunctions (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\analysis\module_with_providers_analyzer.js:72:21)
    at C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\analysis\module_with_providers_analyzer.js:39:33
    at Array.forEach (<anonymous>)
    at ModuleWithProvidersAnalyzer.analyzeProgram (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\analysis\module_with_providers_analyzer.js:38:23)
    at Transformer.analyzeProgram (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\packages\transformer.js:133:45)
    at Transformer.transform (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\packages\transformer.js:76:27)
    at ClusterMaster.onWorkerMessage (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:195:27)
    at C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:55:95
    at ClusterMaster.<anonymous> (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:293:57)
    at step (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:140:27)
    at Object.next (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:121:57)
    at C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:114:75
    at new Promise (<anonymous>)
    at Object.__awaiter (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:110:16)
    at EventEmitter.<anonymous> (C:\Users\marley.powell\source\repos\exclaimercloud-ui\src\ExclaimerCloud.UI\ClientApp\node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:287:32)
    at EventEmitter.emit (events.js:315:20)

Flag to enforce immutable entities

Is it useful to add a flag in the super call of your store that enforces immutable entities? The object containing the data is immutable but the entities itself aren't required to be immutable.

This could simply be checked by adding current !== updated after this.onUpdate and throwing an error depending on the result.

Also: what should the default value for the flag be, if it's wanted. Or no default value?

could not get librabry folder in my project

Hello I tried "npm i @ngxs-labs/entity-state" to run it, it downloads all the dependencies but 'src' folder did not create which contains all library methods. Please let me know how can I get that?

Adding Api Response to Entities

I am getting an array from Get Api call but I am unable to add them in the entities attribute, How can I add in that attribute please let me know?

State of the library

First of all sorry for the complete lack of communication.

As you can already tell this library is basically abandoned at this point. I'm completely out of the loop with what's going on with ngxs-labs & ngxs, and even most of Angular. Unfortunately I don't have the time to properly work on this library.
While I was still working on it, I had massive issues with the project setup and build process, which blocks any kind of updates. Looking at the considerable amount of downloads, I'm happy to review any PRs that help keep the library somewhat useable. Again, I don't have the time or knowledge to fix it myself or create a new maintainable setup.

So unless someone else is willing to take over this library you shouldn't be using it. If nothing changes, I'll archive this repository and deprecate the npm package in a few weeks or months.

Help request / question => how to get an entity by id?

Hi guys. Many thanks for making this lib. I just want to ask: how are you supposed to get an entity by its ID? Have I missed the interface for that or is it not possible? I can only find the interface for getting the nth entity or all entities as an array.

But I want to associate multiple entities (forum-threads and posts) as a graph-relationship and do not want to filter an array for the id every time. I think others have this use-case too.

As far as I understand the implementation of Entity it is a simple JS object with keys and values, where the values are also kept as a sorted array. So (unless it is possible already) why not give read access to the key-value-object?

feat: entity base class

Moved from ngxs/store#321

A base class that you can implement to get methods on your state class to simplify handling crud operations.

It looks something like this.

class Project {
  id: string;
  name: string;
}

class AddOne {
  static readonly type = '[Project] AddProject';
  constructor(public project: Project) {}
}

class AddMany {
  static readonly type = '[Project] AddProjects';
  constructor(public projects: Project[]) {}
}

interface ProjectStateModel extends EntityState<Project> {}

@State<ProjectStateModel>({
  name: 'projects',
  defaults: {
    ...EntityBase.defaults
  }
})
class ProjectState extends EntityBase<Project, ProjectStateModel> {
  // pass injector to base class so that we can get and set state
  // @todo, can we work around this?
  constructor(injector: Injector) {
    super(injector);
  }

  // will automatically do a ctx.setState if needed
  @Action(AddOne)
  addProject(ctx: StateContext<ProjectStateModel>, action: AddOne) {
    this.addOne(action.project);
  }

  @Action(AddMany)
  addProjects(ctx: StateContext<ProjectStateModel>, action: AddMany) {
    this.addMany(action.projects);
  }
}

for now I've only implemented these two, but it shouldn't take me too long to implement the rest of the methods.

Though I would like some input on the api of it.

I would like to be able to get at the StateContext from within the class.
At the moment I'm hacking it by getting this.constructor[META_KEY] and then calling .next on the stateStream.

Please let me know what you think, and if you have any suggestions to improve it even further :)

Feature/Discussion: Collection Sort/Filter

I've gone through the code in this to try and see if I can use it to replace (or get some better ideas on) our homegrown version of doing the same thing (ours has WAY too much boilerplate though and probably much worse performance).

There are a couple of things which most APIs have in common, some of which this repo addresses, however there are use cases I can't see addressed so far. Could I get some feedback on whether these are currently possible, or whether this could be planned for a future update? I'm interested in helping contribute where I can as I would like to use this.

  • Differentiating between a single resource and a collection of resources.
  • A collection may contain filters so as to not return the entire table.
  • A collection may be sorted and return only part of the table.
  • Entities can be related.

Currently I have a state for each API endpoint which contains the following keys:

table: Map<string, T>;
collections: Map<Params, string[]>;

I'm assuming this isn't the most perfect method, however, when requesting a single resource, it is checked first in the table, and then loaded via the service (being saved in the table). When a collection is requested, the parameters for that request are mapped to the identities returned. The individual resources are then saved in the table.

Would this implementation be required on top of this repository, or is this something this repository could accomplish? At the very least the discussion here may produce fruit for those searching for a similar answer, if not provide grounds for documentation.

As for entities being related, this leads into ORM which I'm not sure is a good idea client side, however I can see the benefits.

Thanks.

EntityState selectors recalculate when any part of root state was updated

Instead of repro steps I added test

import { EntityState, EntityStateModel, defaultEntityState, IdStrategy } from '@ngxs-labs/entity-state';
import { State, NgxsModule, Store, Action, StateContext } from '@ngxs/store';
import { TestBed } from '@angular/core/testing';

fdescribe('EntityState selectors recalculate when not needed', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        NgxsModule.forRoot([
          SomeEntityState,
          AnotherState
        ])
      ]
    });
  });

  it('get entities once when updating different state', () => {
    const spy = jasmine.createSpy('callback');
    const store = TestBed.get(Store) as Store;
    store.select(SomeEntityState.entities).subscribe(spy);
    store.dispatch(new UpdateAnotherState);
    store.dispatch(new UpdateAnotherState);
    expect(spy).toHaveBeenCalledTimes(1); // Expected spy callback to have been called once. It was called 3 times.
  });
});

@State<EntityStateModel<any>>({
  name: 'someEntity',
  defaults: defaultEntityState<any>()
})
class SomeEntityState extends EntityState<any> {
 constructor() {
  super(SomeEntityState, 'id', IdStrategy.EntityIdGenerator);
 }
}

class UpdateAnotherState {
  static type = 'UpdateAnotherState';
}

@State<number>({
  name: 'another',
  defaults: 999
})
class AnotherState {
 constructor() {
 }
 @Action(UpdateAnotherState)
 update(ctx: StateContext<number>) {
  ctx.setState(Math.random());
 }
}

Entity Adapter

A while back I prototyped something like John Papas NGRX-DATA, https://gist.github.com/amcdnl/cafd37bc9a99e2cd7653ebc023e06ffe#file-readme-md

Something we might want to think about one day.

I have created a LIB to be used as follows. I'm using it on mine.
projects:

How to use:

export class UserStateModel extends NgxsEntityStateModel<UserModel> {}

@State<UserStateModel>({
  name: 'user',
  defaults: UserStateModel.InitialState()
})
export class UserState implements NgxsOnInit {}

Methods Available:
NgxsEntityAdapter.addAll( payload, ctx );
NgxsEntityAdapter.addOne( payload, ctx );
NgxsEntityAdapter.updateOne( payload, ctx );
NgxsEntityAdapter.removeOne( payload, ctx );

NgxsEntityStateModel Class:

export class NgxsEntityStateModel<T> {
  public ids: string[];
  public entities: { [id: string]: T };
  public selected: T | null;
  public isLoading: boolean;

  static InitialState() {
    return {
      ids: [],
      entities: {},
      selected: null,
      isLoading: false
    };
  }
}

ngxs/store#541 (comment)

Provide beta release?

We are using this in a project close to production and are currently building this ourselves.
Is it possible to provide it as NPM package with beta/alpha tag?

Provide filter operators for actions e.g. ofEntityAction

I added some filters like this, they could be provided by the library.

import { Type } from "@angular/core";
import { EntityState } from "@ngxs-labs/entity-state";
import { ofActionSuccessful } from "@ngxs/store";

export enum EntityActionType {
  SetActive = "setActive"
  ...
}

export const ofEntityActionSuccessful = (state: Type<EntityState<any>>, actionType: EntityActionType) => {
  const type = `[${(state as any).NGXS_META.path}] ${actionType}`;
  return ofActionSuccessful({
    type: type
  });
};
...

Behaviour of remove action

Should the remove action remove all entities, if the payload is strictly === null or should this functionality be moved to a separate removeAll action?

Allow numbers as keys

As dictionaries with numbers are a lot faster, and I could not think of any advantages, I use numbers as keys.

However, entity-state seems to allow only strings as keys. For instance, EntitySelector<T> is restricted to string-types (except when you use a custom lambda).

/**
 * An EntitySelector determines which entities will be affected.
 * Can be one of the following:
 * - a single ID in form of a string
 * - multiple IDs in form of an array of strings
 * - a predicate function that returns `true` for entities to be selected
 */
export declare type EntitySelector<T> = string | string[] | ((entity: T) => boolean);

It would be great if the restrictions could be just a little bit relaxed by extending such types to numbers.

Make pagination feature optional

First of all thanks for the great work !

As pagination is very specific to every application, I'd suggest that this library should best not assume any kind of implementation. It'd be easier to add your own implementation than work around the provided one.

Not working developmentMode, getting ERROR TypeError: Cannot add property VALUE_OF_ID, object is not extensible

Steps to reproduce: set

    NgxsModule.forRoot(states, {
      developmentMode: true
    }),

in the example project and you will get

ERROR TypeError: Cannot add property VALUE_OF_ID, object is not extensible
    at entity-state.ts:471

(if you use AddOrReplace action), eg at entities[id] = entity;

It would be nice to have an error message suggesting to disable development mode if entity doesn't work with immutability (?)

Change UMD Bundling from ES2015 to ES5?

I am getting an error when trying to use ngcc in conjunction with this package, and according to the below post it has to do with the way the package is being bundled. This package is currently bundled in ES2015 format and ngcc uses ES5. Is there a reason this is configured for ES2015, or can it be changed to ES5?

angular/angular#40178

Angular 9 upgrade blocked

When upgrading to angular 9 the next incompatible peer dependency error appears:

Package "@ngxs-labs/entity-state" has an incompatible peer dependency to "@angular/core" (requires "^6.0.0 || ^7.0.0" (extended), would install "9.1.12").

Could you please update it?

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on Greenkeeper branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please click the 'fix repo' button on account.greenkeeper.io.

Arrays or maps of SubStates

I think this should be controlled at the entity state level.

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[x ] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[ ] Other... Please describe:

Current behavior

Currently you can't create a SubState within an array or map.

Expected behavior

I would like to be able to use a Map of RowStates in a StateModel:

import {State} from '@ngxs/store';

export interface RowStateModel {
  id: number; // Or string
}

export interface GridStateModel {
  id: number; // Or string
  rows: Map<number, RowState>;
}

/**
 * GridCollectionStateModel has a map of GridStates,
 * where the key refers to the dataname of the GridStateModel.
 */
export interface GridCollectionStateModel {
  grids: Map<number, GridState>;
}

@State<RowStateModel>({
  name: 'row',
  defaults: {
    id: -1
  }
})
export class RowState {}

@State<GridStateModel>({
  name: 'grid',
  defaults: {
    id: -1,
    rows: new Map<number, RowState>()
  }
})
export class GridState {}

@State<GridCollectionStateModel>({
  name: 'grid-collection',
  defaults: {
    grids: new Map<number, GridState>()
  }
})
export class GridCollectionState {}

This should create a state that looks like this:

{
  "grids": {
    "GridState_0": {
      "id": 0,
      "rows": {
        "RowState_0": {
          "id": 0
        },
        "RowState_1": {
          "id": 1
        }
      }
    },
    "GridState_1": {
      "id": 1,
      "rows": {
        "RowState_0": {
          "id": 0
        }
      }
    }
  }
}

What is the motivation / use case for changing the behavior?

In my application I have a use case where I have a complex data tree which would need to have a SubState. I have a collection of grids that can be displayed at the same time. Each grid has a dataname (unique identifier), which it uses to fetch the corresponding data. On top of that, each grid has a set of hierarchical rows, for which I also could use the SubStates.

The `onUpdate` method and entity types

I noticed two contradicting thoughts: I made the onUpdate method abstract, because I thought maybe someone wants a state with just strings or something. But on the other hand I expect them to pass a idKey in the super call and all the code works based on entity[idKey]. Also: what if the entities can't be used properly with the entity[idKey] syntax. What if a function should be called instead?


Which raises the following questions:

  1. Should the <T> in EntityState<T> have constraints like <T extends {}>?
  2. Should the onUpdate method be non-abstract and provide a default implementation that returns {...current, ...updated}, to further reduce boilerplate? The user would still have the possibility to overwrite this, if spreading does not work for the entity type.
  3. Should you rather pass a function to determine the key of an entity, instead of a string? Or overwrite the idOf method?

So far I only used object literals for my states, so I'm not sure if the current implementation fits all needs. For example would this work with ImmutableJS data structures?

Would love to hear some opinions and experiences.

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on Greenkeeper branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please click the 'fix repo' button on account.greenkeeper.io.

Library's peerDependencies

{
  "name": "entity-state",
  "version": "0.0.1",
  "peerDependencies": {
    "@angular/common": "^7.0.0",
    "@angular/core": "^7.0.0",
    "@ngxs/store": "^3.3.0"
  }
}

There's a reason why not support angular 6 and older ngxs versions?

Syntax discussion

Discuss and decide on one of the following syntax for dispatching actions:

// 1. Misses new keyword but has type information for payload
this.store.dispatch(UpdateActive(TodoState, { done: true }));

// 2. Looks more like the usual ngxs way but no type information for payload
this.store.dispatch(new TodoState.updateActive({ done: true }));

Also consider: Should the action be upper or lowercase? Change order of parameters?

Joining Selectors of two EntityState not working

Well i was trying to mix the states in a selector, i try Static and Dynamic but neither of them work.
I get as first value an empty array, which is correct, and after i get undefined. Both states after update have all the values.

I compiled the current master of the entity-state to include it as library.

// AuthorState extends EntityState<AuthorDTO>
    @Selector([LocationState])
    static entities2(
        state: EntityStateModel<AuthorDTO>,
        locationState: EntityStateModel<LocationDTO>
    ) {
        return Object.values(state.entities)
        .map(v => {
            v.bornLocation = locationState.entities[v.bornIdlocation];
            return v;
        });
    }


    static entities3() {
        return createSelector([this, LocationState], (state: EntityStateModel<AuthorDTO>, location: EntityStateModel<LocationDTO>) => {
            return Object.values(state.entities)
            .map(v => {
                v.bornLocation = location.entities[v.bornIdlocation];
                return v;
            });
        });
    }


//And later in the component 
    @Select(AuthorState.entities2)
    author1$: Observable<AuthorDTO[]>;
    @Select(AuthorState.entities3())
    author2$: Observable<AuthorDTO[]>;

Cannot find module 'entity-store'

It's possible to build the library but the integration app is unable to compile. (npm run lib && ng build)

ERROR in src/app/app.component.ts(3,125): error TS2307: Cannot find module 'entity-store'.
src/app/app.component.ts(14,21): error TS2339: Property 'size' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(15,21): error TS2339: Property 'entities' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(16,21): error TS2339: Property 'active' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(17,21): error TS2339: Property 'activeId' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(18,21): error TS2339: Property 'keys' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(19,21): error TS2339: Property 'loading' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(20,21): error TS2339: Property 'error' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(36,39): error TS2339: Property 'remove' does not exist on type 'typeof TodoState'.
src/app/app.component.ts(40,39): error TS2339: Property 'remove' does not exist on type 'typeof TodoState'.
src/app/store/todo/store.ts(2,65): error TS2307: Cannot find module 'entity-store'.

Proposal: Entity state decorator

Proposal

A kind of state decorator to implement a standardized CRUD action mutators and selectors for an entity collection.

The decorator would receive the action classes for create, update, destroy and would map them to internal methods, and would expose some selectors for retrieving one, many and all entities and maybe a helper for filters

export interface Student {
  name: string;
  grade: number;
}

@EntityState<Student>({
  name: 'students',
  actions: {
    create: [StudentAdd, StudentFullLoad],
    destroy: [StudentDelete]
  }, 
  defaults: {
    // Decorator injects automatically the collection (array) of entities, and so on ...
    selectedId: number,
  },
})
class StudentState {} 

Actions

export class StudentAdd {
  static readonly type = '[Student] Add'
  constructor (public payload: Student) {}
}

export class StudentFullLoad {
  static readonly type = '[Student] Full Load'
  constructor (public payload: Student[]) {}
}

export class StudentDelete {
  static readonly type = '[Student] Delete'
  constructor (public id: number) {}
}

Use of selectors

const all = this.store.select(StudentState.retrieveAll)
const one = this.store.select(StudentState.retrieve(5))

or

const all = this.store.select(EntityState.retrieveAll<StudentState>)
const one = this.store.select(EntityState.retrieve<StudentState>(5))

I'm not sure if the current version of typescript could implement that... Any ideas will be welcome!

[Feature request] IdStrategy with nested keys

It would be nice to be able to generate id from some nested object of the entity.

I saw in some other similar implementations of entity adapters the possibility to pass a function for generating the id.

Is this possible?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.