Я начинаю использовать новый API сигналов Angular. Но мне сказали, что при использовании сигналов нам нужно относиться к данным как к неизменяемым.
И я не уверен, смогу ли я сделать что-то подобное с JS Maps:
// code in my service.
// I'm using this Map to save the messages in a object and get the messages by queue name.
messages = signal(new Map<string, Object>());
queryForMessagesInQueue(queue) {
this.httpClient.get('...').subscribe((newMessagesArray) => {
const messages = this.messages().get(queue) || {};
// validate if newMessages already exists in the message object
let addedMessages = 0;
newMessagesArray.forEach((newMsg) => {
const messageExists = !!messages[newMsg.id]
if (messageExists == false) {
messages[newMsg.id] = newMsg;
addedMessages++;
}
});
if (addedMessages !== 0) {
this.messages.update((msgMap) => msgMap.set(queue, messages));
}
});
}
Затем в своих компонентах я делаю что-то вроде этого:
export class MyComponent {
private service = inject(MyService)
messages = service.messages; // this is the service signal.
}
Правильно ли я его использую?
Можно ли использовать сигналы со структурами данных, такими как JS Maps? Будет ли корректно работать обнаружение изменений?
Заранее спасибо.
@Chellappan Мое внимание привлекло то, что при использовании ChangeDetectionStrategy для OnPush обнаружение изменений не работало должным образом.
Вы можете переопределить функции равенства знаков с помощью собственного компаратора: kurtwanger40.medium.com/….
@Chellappanவ это была моя оригинальная идея. Но при проверке на равенство оба значения всегда одинаковы, поскольку «старое» значение Map и «новое» значение Map являются одной и той же ссылкой.
Если возможно, можете ли вы создать пример примера в stack blitz?
Вы просите меня или ОП создать? Вот stackblitz для иллюстрации.





По умолчанию сигналы используют ссылочное равенство (=== сравнение). В this.messages.update вы возвращаете одно и то же Map, поэтому при проверке равенства «старое» значение Map и «новое» значение Map являются одной и той же ссылкой.
Поэтому вам нужно вернуть новый Map, чтобы ваш код работал. Кроме того, вам действительно не нужен msgMap.set(queue, messages), потому что вы уже добавили значения к messages и поэтому устанавливаете ту же ссылку messages.
Окончательный код выглядит так:
messages = signal(new Map<string, Object>());
queryForMessagesInQueue(queue) {
this.httpClient.get('...').subscribe((newMessages) => {
const messages = this.messages().get(queue) || {};
const addedMessages = newMessages.filter((newMsg) => !!messages[newMsg.id]);
if (addedMessages.length) {
addedMessages.forEach((addedMsg) => messages[addedMsg.id] = addedMsg);
this.message.update((msgMap) => new Map(msgMap));
}
});
}
Я бы также заменил Object на Record<string, Message> или индексную подпись { [messageId: string]: Message }.
Это сработало, как и ожидалось! Спасибо за предложение, исходный код немного сложнее, чем тот, о котором идет речь, я постарался максимально исключить бизнес-логику и интерфейсы.
Должно работать, есть ли у вас какие-либо проблемы?