Skip to content

Commit 0106e93

Browse files
fix(signals): allow modifying entity id on update (#4404)
1 parent 1b1cf5b commit 0106e93

File tree

8 files changed

+591
-52
lines changed

8 files changed

+591
-52
lines changed

modules/signals/entities/spec/updaters/update-all-entities.spec.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ describe('updateAllEntities', () => {
5151

5252
patchState(
5353
store,
54-
updateAllEntities({ text: '' }),
55-
updateAllEntities((todo) => ({ completed: !todo.completed }))
54+
updateAllEntities({ text: '' }, { selectId: (todo) => todo._id }),
55+
updateAllEntities((todo) => ({ completed: !todo.completed }), {
56+
selectId: (todo) => todo._id,
57+
})
5658
);
5759

5860
expect(store.entityMap()).toBe(entityMap);
@@ -79,7 +81,13 @@ describe('updateAllEntities', () => {
7981
collection: 'todo',
8082
selectId: selectTodoId,
8183
}),
82-
updateAllEntities({ completed: false }, { collection: 'todo' })
84+
updateAllEntities(
85+
{ completed: false },
86+
{
87+
collection: 'todo',
88+
selectId: (todo) => todo._id,
89+
}
90+
)
8391
);
8492

8593
expect(store.todoEntityMap()).toEqual({
@@ -98,6 +106,7 @@ describe('updateAllEntities', () => {
98106
store,
99107
updateAllEntities(({ completed }) => ({ completed: !completed }), {
100108
collection: 'todo',
109+
selectId: (todo) => todo._id,
101110
})
102111
);
103112

modules/signals/entities/spec/updaters/update-entities.spec.ts

Lines changed: 239 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,20 @@ describe('updateEntities', () => {
5050

5151
patchState(
5252
store,
53-
updateEntities({
54-
predicate: (todo) => todo.text.startsWith('Buy'),
55-
changes: { completed: false },
56-
}),
57-
updateEntities({
58-
predicate: ({ completed }) => !completed,
59-
changes: ({ text }) => ({ text: `Don't ${text}` }),
60-
})
53+
updateEntities(
54+
{
55+
predicate: (todo) => todo.text.startsWith('Buy'),
56+
changes: { completed: false },
57+
},
58+
{ selectId: (todo) => todo._id }
59+
),
60+
updateEntities(
61+
{
62+
predicate: ({ completed }) => !completed,
63+
changes: ({ text }) => ({ text: `Don't ${text}` }),
64+
},
65+
{ selectId: (todo) => todo._id }
66+
)
6167
);
6268

6369
expect(store.entityMap()).toEqual({
@@ -228,4 +234,229 @@ describe('updateEntities', () => {
228234
expect(store.todoIds()).toEqual(['x', 'y', 'z']);
229235
expect(store.todoEntities()).toEqual([todo1, todo2, todo3]);
230236
});
237+
238+
it('updates entity ids', () => {
239+
const Store = signalStore(withEntities<User>());
240+
const store = new Store();
241+
242+
patchState(
243+
store,
244+
addEntities([user1, user2, user3]),
245+
updateEntities({
246+
ids: [user1.id, user2.id],
247+
changes: ({ id }) => ({ id: id + 10, firstName: `Jimmy${id}` }),
248+
}),
249+
updateEntities({
250+
ids: [user3.id],
251+
changes: { id: 303, lastName: 'Hendrix' },
252+
})
253+
);
254+
255+
expect(store.entityMap()).toEqual({
256+
11: { ...user1, id: 11, firstName: 'Jimmy1' },
257+
12: { ...user2, id: 12, firstName: 'Jimmy2' },
258+
303: { ...user3, id: 303, lastName: 'Hendrix' },
259+
});
260+
expect(store.ids()).toEqual([11, 12, 303]);
261+
262+
patchState(
263+
store,
264+
updateEntities({
265+
predicate: ({ id }) => id > 300,
266+
changes: ({ id }) => ({ id: id - 300 }),
267+
}),
268+
updateEntities({
269+
predicate: ({ firstName }) => firstName === 'Jimmy1',
270+
changes: { id: 1, firstName: 'Jimmy' },
271+
})
272+
);
273+
274+
expect(store.entityMap()).toEqual({
275+
1: { ...user1, id: 1, firstName: 'Jimmy' },
276+
12: { ...user2, id: 12, firstName: 'Jimmy2' },
277+
3: { ...user3, id: 3, lastName: 'Hendrix' },
278+
});
279+
expect(store.ids()).toEqual([1, 12, 3]);
280+
});
281+
282+
it('updates custom entity ids', () => {
283+
const Store = signalStore(withEntities<Todo>());
284+
const store = new Store();
285+
286+
patchState(
287+
store,
288+
addEntities([todo1, todo2, todo3], { selectId: (todo) => todo._id }),
289+
updateEntities(
290+
{
291+
ids: [todo1._id, todo2._id],
292+
changes: ({ _id }) => ({ _id: _id + 10, text: `Todo ${_id}` }),
293+
},
294+
{ selectId: (todo) => todo._id }
295+
),
296+
updateEntities(
297+
{
298+
ids: [todo3._id],
299+
changes: { _id: 'z30' },
300+
},
301+
{ selectId: (todo) => todo._id }
302+
)
303+
);
304+
305+
expect(store.entityMap()).toEqual({
306+
x10: { ...todo1, _id: 'x10', text: 'Todo x' },
307+
y10: { ...todo2, _id: 'y10', text: 'Todo y' },
308+
z30: { ...todo3, _id: 'z30' },
309+
});
310+
expect(store.ids()).toEqual(['x10', 'y10', 'z30']);
311+
312+
patchState(
313+
store,
314+
updateEntities(
315+
{
316+
predicate: ({ text }) => text.startsWith('Todo '),
317+
changes: ({ _id }) => ({ _id: `${_id}0` }),
318+
},
319+
{ selectId: (todo) => todo._id }
320+
),
321+
updateEntities(
322+
{
323+
predicate: ({ _id }) => _id === 'z30',
324+
changes: { _id: 'z' },
325+
},
326+
{ selectId: (todo) => todo._id }
327+
)
328+
);
329+
330+
expect(store.entityMap()).toEqual({
331+
x100: { ...todo1, _id: 'x100', text: 'Todo x' },
332+
y100: { ...todo2, _id: 'y100', text: 'Todo y' },
333+
z: { ...todo3, _id: 'z' },
334+
});
335+
expect(store.ids()).toEqual(['x100', 'y100', 'z']);
336+
});
337+
338+
it('updates entity ids from specified collection', () => {
339+
const Store = signalStore(
340+
withEntities({
341+
entity: type<User>(),
342+
collection: 'user',
343+
})
344+
);
345+
const store = new Store();
346+
347+
patchState(
348+
store,
349+
addEntities([user1, user2, user3], { collection: 'user' }),
350+
updateEntities(
351+
{
352+
ids: [user1.id, user2.id],
353+
changes: ({ id }) => ({ id: id + 100, firstName: `Jimmy${id}` }),
354+
},
355+
{ collection: 'user' }
356+
),
357+
updateEntities(
358+
{
359+
ids: [user3.id],
360+
changes: { id: 303, lastName: 'Hendrix' },
361+
},
362+
{ collection: 'user' }
363+
)
364+
);
365+
366+
expect(store.userEntityMap()).toEqual({
367+
101: { ...user1, id: 101, firstName: 'Jimmy1' },
368+
102: { ...user2, id: 102, firstName: 'Jimmy2' },
369+
303: { ...user3, id: 303, lastName: 'Hendrix' },
370+
});
371+
expect(store.userIds()).toEqual([101, 102, 303]);
372+
373+
patchState(
374+
store,
375+
updateEntities(
376+
{
377+
predicate: ({ id }) => id > 300,
378+
changes: ({ id }) => ({ id: id - 300 }),
379+
},
380+
{ collection: 'user' }
381+
),
382+
updateEntities(
383+
{
384+
predicate: ({ firstName }) => firstName === 'Jimmy1',
385+
changes: { id: 1, firstName: 'Jimmy' },
386+
},
387+
{ collection: 'user' }
388+
)
389+
);
390+
391+
expect(store.userEntityMap()).toEqual({
392+
1: { ...user1, id: 1, firstName: 'Jimmy' },
393+
102: { ...user2, id: 102, firstName: 'Jimmy2' },
394+
3: { ...user3, id: 3, lastName: 'Hendrix' },
395+
});
396+
expect(store.userIds()).toEqual([1, 102, 3]);
397+
});
398+
399+
it('updates custom entity ids from specified collection', () => {
400+
const Store = signalStore(
401+
withEntities({
402+
entity: type<Todo>(),
403+
collection: 'todo',
404+
})
405+
);
406+
const store = new Store();
407+
408+
patchState(
409+
store,
410+
addEntities([todo1, todo2, todo3], {
411+
collection: 'todo',
412+
selectId: (todo) => todo._id,
413+
}),
414+
updateEntities(
415+
{
416+
ids: [todo1._id, todo2._id],
417+
changes: ({ _id }) => ({ _id: _id + 10, text: `Todo ${_id}` }),
418+
},
419+
{ collection: 'todo', selectId: (todo) => todo._id }
420+
),
421+
updateEntities(
422+
{
423+
ids: [todo3._id],
424+
changes: { _id: 'z30' },
425+
},
426+
{ collection: 'todo', selectId: (todo) => todo._id }
427+
)
428+
);
429+
430+
expect(store.todoEntityMap()).toEqual({
431+
x10: { ...todo1, _id: 'x10', text: 'Todo x' },
432+
y10: { ...todo2, _id: 'y10', text: 'Todo y' },
433+
z30: { ...todo3, _id: 'z30' },
434+
});
435+
expect(store.todoIds()).toEqual(['x10', 'y10', 'z30']);
436+
437+
patchState(
438+
store,
439+
updateEntities(
440+
{
441+
predicate: ({ text }) => text.startsWith('Todo '),
442+
changes: ({ _id }) => ({ _id: `${_id}0` }),
443+
},
444+
{ collection: 'todo', selectId: (todo) => todo._id }
445+
),
446+
updateEntities(
447+
{
448+
predicate: ({ _id }) => _id === 'z30',
449+
changes: { _id: 'z' },
450+
},
451+
{ collection: 'todo', selectId: (todo) => todo._id }
452+
)
453+
);
454+
455+
expect(store.todoEntityMap()).toEqual({
456+
x100: { ...todo1, _id: 'x100', text: 'Todo x' },
457+
y100: { ...todo2, _id: 'y100', text: 'Todo y' },
458+
z: { ...todo3, _id: 'z' },
459+
});
460+
expect(store.todoIds()).toEqual(['x100', 'y100', 'z']);
461+
});
231462
});

0 commit comments

Comments
 (0)