Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(signals): add entities subpackage #4090

Merged
merged 4 commits into from
Nov 8, 2023
Merged

Conversation

markostanimirovic
Copy link
Member

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Documentation content changes
[ ] Other... Please describe:

What is the current behavior?

Closes #3996

What is the new behavior?

  1. Adding entity state to the store:
const UsersStore = signalStore(
  withEntities<User>(),
);

const usersStore = inject(UsersStore);

const entityMap = usersStore.entityMap(); // EntityMap<User>
const ids = usersStore.ids(); // EntityId[]
const entities = usersStore.entities(); // User[]
  1. Adding named entity state to the store:
const UsersStore = signalStore(
  withEntities({ entity: type<User>(), collection: 'user' })
);

const usersStore = inject(UsersStore);

const entityMap = usersStore.userEntityMap(); // EntityMap<User>
const ids = usersStore.userIds(); // EntityId[]
const entities = usersStore.userEntities(); // User[]
  1. Available updaters:
  • addEntity
  • addEntities
  • updateEntity
  • updateEntities
  • updateAllEntities
  • setEntity
  • setEntities
  • setAllEntities
  • removeEntity
  • removeEntities
  • removeAllEntities
patchState(
  usersStore,
  updateEntities({
    predicate: ({ name }) => name.contains('m'),
    changes: (user) => ({ name: user.name + 's' }) 
  })
);

// different id key:
patchState(
  usersStore,
  setEntity({ _id: 1, name: 'Marko' }, { idKey: '_id' })
);

// named entity state
patchState(
  usersStore,
  updateAllEntities({ name: 'Tim' }, { collection: 'user' })
);

Does this PR introduce a breaking change?

[ ] Yes
[x] No

@netlify
Copy link

netlify bot commented Oct 22, 2023

Deploy Preview for ngrx-io ready!

Name Link
🔨 Latest commit 1519299
🔍 Latest deploy log https://app.netlify.com/sites/ngrx-io/deploys/65417c0f0000cf0008150d10
😎 Deploy Preview https://deploy-preview-4090--ngrx-io.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@markostanimirovic markostanimirovic marked this pull request as ready for review October 23, 2023 00:17
Copy link
Member

@timdeschryver timdeschryver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice Marko! 🏆


export type EntityState<Entity> = {
entityMap: EntityMap<Entity>;
ids: EntityId[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would mean that we support id arrays that has numbers and strings.
Do we want this?
I think it would be better to only allow one kind:

export type EntityId = string | number;

const ids: EntityId[] = [1, '2', 3];
//    ^ OK
const idsOther: (string[] | number[]) = [1, '2', 3];
//    ^ Type '(string | number)[]' is not assignable to type 'string[] | number[]'.

https://www.typescriptlang.org/play?#code/FAUwHgDg9gTgLgAjgTwiBBRAdnAligSQBMEBeBAZzhlywHMEAfBLAVwFsAjEGAbmGABjKFioJcRCgC5MOfMmIBtALpkEigIwAaBAHIATLp0BmZf2GjEEigHk4ACx4yAFFRr0VTFh24wVASjVNHQMjBFN+IA

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review Tim! 🙏

Yes, I was aware of this behavior. I was wondering if it's worth defining a different type for EntityIds as you mentioned because both string and number can be used to get the record property value by key.

For example, this code will compile:

const record: Record<number, number> = {
  '1': 1,
};

although we specified that record keys can be only numbers, because in JavaScript this works:

const record = { 1: 1 };

console.log(record[1]); // logs 1
console.log(record['1']); // logs 1

Let me know if you think this should be changed and I'll add the EntityIds type:

type EntityIds = string[] | number[];

cc @brandonroberts

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with both options, I added this comment just to be sure that it was intended.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think allowing strings as record keys should be supported for use cases such as uuids.

modules/signals/entities/src/with-entities.ts Show resolved Hide resolved
modules/signals/entities/src/with-entities.ts Show resolved Hide resolved
modules/signals/src/index.ts Show resolved Hide resolved
@brandonroberts brandonroberts merged commit f01bcd1 into main Nov 8, 2023
4 checks passed
@brandonroberts brandonroberts deleted the feat/signals/entities branch November 8, 2023 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

@ngrx/signals: Add entities sub-package
3 participants