SyncModel
SyncModel - Хелпер для синхронизации одной модели в разных местах приложения
Использование
Типичный пример использования класса, несколько списков содержащих одну модель, но из разных источников (к примеру списки из asyncData и pinia). При обновлении одного из них, необходимо обновлять второй.
- Если
SyncModelинстанс еще не создан, его нужно создать
export interface IFoo {
uid: number;
text: string;
}
export const fooSyncModel = SyncModel.extend<IFoo>({
eventName: 'foo',
itemKey: 'uid',
});
- Подписаться на изменение модели
import { fooSyncModel, type IFoo } from '.../foo-sync-model';
import { useSyncModelSubscribe } from 'shared-front/lib/composition/sync-model';
export default {
setup() {
const foos = ref([] as IFoo[]);
let unsubFoo = () => {};
onMounted(() => {
unsubFoo = fooSyncModel.subsribe({
get: () => foos.value,
});
});
onUnmounted(unsubFoo);
// Or
useSyncModelSubscribe(fooSyncModel, {
get: () => foos.value,
});
},
// Or
data: () => ({
foos: [] as IFoo[],
}),
mounted() {
this.$once(
'hook:beforeDestroy',
fooSyncModel.subsribe({
get: () => this.foos,
}),
);
},
};
1.1 Подписка если модель не совпадает с исходной
import { fooSyncModel } from '.../foo-sync-model';
interface IBar {
uid: number;
body: string;
}
export default {
setup() {
const bars = ref([] as IBar[]);
onMounted(() => {
onUnmounted(fooSyncModel.subsribe({
get: () => bars.value,
merge: (oldValue, payload) => {
const newValue: IBar = {
...oldValue,
};
if (payload.text) newValue.body = payload.text;
return newValue;
},
}));
});
},
};
- Обновление модели
import { fooSyncModel } from '.../foo-sync-model';
export default {
setup() {
fooSyncModel.update({
uid: 1,
text: 'foo'
});
fooSyncModel.updateArray([
{
uid: 1,
text: 'foo 1'
},
{
uid: 2,
text: 'foo 2'
},
]);
},
}
constructor/extend
Перед использованием необходимо создать модель с нужными параметрами
const syncModel = new SyncModel({
eventName: 'forum:theme',
});
| Prop | Required | Default | Description |
|---|---|---|---|
| eventName | true | - | Уникальное название события по которому будет синхронизироваться модель |
| itemKey | false* | 'id' | Название уникального поля в модели, если в модели нет поля id, itemKey обязательно |
Из-за ограничений typescript, для поддержки типов нужно использовать метод extend
interface Theme {
uuid: string;
name: string;
}
const syncModel = SyncModel.extend<Theme>()({
eventName: 'theme',
itemKey: 'uuid',
});
Подписка на обновления
let theme;
const unSubscribe = syncModel.subscribe({
get: () => theme,
set: ({ newValue }) => {
theme = newValue;
},
});
Для подписки на обновления нужно указать объект/массив с сущностью и опционально set
Возвращается функция для отписки
Если не указан
set, для массивов будет использованspliceна массиве Для объектов по умолчанию будет применятьсяlodash/merge
| Prop | Required | Default | Descripton |
|---|---|---|---|
| get | true | - | Геттер для получения текущего значения сущности, в дальнейшем используется для сравнения с обновленной сущностью(обновляться может сущность с иным id) |
| set | false | Описано выше | Сеттер для сущности, в случае если id из геттера не совпадает/id из геттера не найден в массиве, сеттер не вызывается |
| source | false | undefined | Если в подписке указан source и он совпадает с source из update то событие игнорируется для данной подписки |
| server | false | false | По умолчанию, подписка не происходит на сервере, при установке флага в true, подписка произойдет, Warning для сервера обязательно отписываться от событий при server: true, иначе будет утечка памяти |
| merge | false* | lodash/merge | Функция для слияния старого и нового значения, Функция принимает 2 аргумента, старое значение (oldValue) и payload. Если в get модель отличается от update, то поле обязательно. |
set array
Аргументы сеттера если из геттера вернулся массив и в нем найдена сущность с обновляемым идентификатором
| Prop | Descripton |
|---|---|
| id | Идентификатор обновляемой сущности. *Название поля не меняется при изменении itemKey |
| payload | Обновляемая часть, может как целиком перезаписывать сущность так и лишь одно поле, не содержит идентификатор |
| oldValue | Предыдущее значение сущности |
| newValue | Новое значение сущности, результат вызова функции merge |
| array | Массив в котором содержится сущность |
| index | Индекс сущности в array |
set object
Аргументы сеттера если из геттера вернулся объект и у сущностей совпадают идентификаторы
| Prop | Descripton |
|---|---|
| id | Идентификатор обновляемой сущности. *Название поля не меняется при изменении itemKey |
| payload | Обновляемая часть, может как целиком перезаписывать сущность так и лишь одно поле, не содержит идентификатор |
| oldValue | Предыдущее значение сущности |
| newValue | Новое значение сущности, результат вызова функции merge |
merge example
interface IFoo {
id: number;
text: string;
}
interface IBar {
id: number;
body: string;
}
const fooModel = SyncModel.extend<IFoo>()({
eventName: 'foo',
});
const bars: IBar[] = [];
const unSubscribe = fooModel.subscribe({
get: () => bars,
merge: (oldValue, payload) => {
const newValue: IBar = {
...oldValue,
};
if (payload.text) newValue.body = payload.text;
return newValue;
},
});
Отписка от обновлений
const unSubscribe = syncModel.subscribe(...);
...
unSubscribe();
Для отписки от обновлений нужно вызвать функцию возращенную из метода subscribe
Обновление сущностей
interface Theme {
id: number;
name: string;
body: string;
}
syncModel.update({
id: 1,
name: 'New name',
});
syncModel.updateArray([
{
id: 1,
name: 'New name',
},
{
id: 2,
name: 'New name',
}
]);
Обновление сущностей осуществляется через вызов методов update/updateArray, API полностью идентичны, но updateArray принимает массив сущностей для обновления.
Модификаторы
syncModel.update({
id: 1,
name: 'New name',
}, { source: 'vuex' });
syncModel.updateArray([
{
id: 1,
name: 'New name',
},
{
id: 2,
name: 'New name',
}
], { source: 'vuex' });
Так же у обновлений могут быть модификаторы
| Name | Description |
|---|---|
| source | Фильтр для событий из одного источника, при совпадении источника в update и subscribe, обновление игнорируется |