Presenter
import: @azure-net/kit/edges
Презентеров существует два вида - createPresenter и createPresenterFactory, презентеры являются провайдерами ui-логики для компонентов. Большинство ui-логики которую вы можете хранить за пределами компонента лучше хранить в презентере, а также реализовывать методы общения с application слоем. Одной из главных особенностей является то что презентеры полноценно дружат с ssr и на стороне сервера создаются в отдельном контексте с помощью AsyncLocalStorage, это позволяет не бояться за то что ваши переменные и состояния утекут между пользователями, все что находится в презентере полностью ssr-безопасно. Важно учитывать что для работы с презентерами в ssr проекте вы обязаны настроить edges-svelte с помощью edgesHandle (смотри раздел Настройка проекта). Кэширование презентеров позволяет не инициализировать презентеры заново при каждом новом обращении, что позволяет ускорить повторную инициализацию в разы. Реализация позволяет использовать презентеры внутри презентеров и инджектить их друг в друга, также важно понимать что при вызове перезентера вы обязаны находиться в индивидуальном контексте юзера созданном через asyncLocalStorage, если вы на стороне сервера, либо на клиенте, если вы на клиентской стороне, а значит не вызывайте их на верхнем уровне ts файлов, вызывайте внутри компонентов, дргуих презентеров и функций которые вызываете внутри компонентов или презентеров. Вызов презентера за пределами контекста юзера (AsyncLocalStorage) на стороне сервера вызовет ошибки. Презентеры можно генерировать с помощью @azure-net/cli.
createPresenter
Является базовым видом презентера принимает уникальный в рамках проекта ключ как имя, а также функцию-фабрику внутри которой мы и описываем и возвращаем нашу ui-логику.
createPresenterFactory
Является фабрикой для создания презентеров. Главная задача этого метода в создании презентеров с уже добавленными зависимостями. Предположим у вас есть некая функция которую вы используете постоянно во всех презентерах, тогда вам проще добавить ее в createPresenterFactory, а потом создавать провайдеры с помощью данной фабрики. Список презентеров созданных с помощью фабрик вы можете хранить в core/presenters. Также спешу заметить что создание презентеров с помощью фабрик является приоритетным методом работы с презентерами, так как на старте возможно вам зависимости не понадобятся, но через какое то время могут понадобится, поэтому стоит позаботиться об этом заранее. Также немалое количество особенностей пакета вы потеряете если не будете использовать AsyncHelpers (о них в разделах дальше), рекомендуется на начальном этапе создать презентер через фабрику и добавить их как зависимости.
Примеры и описание работы
const AuthPresenter = createPresenter(`ContextNameAuthPresenter`, () => {
const { AuthService } = ApplicationProvider();
const modal = modalManager();
const login = async (request: ILoinRequest) => await AuthService.login(request);
const showLoginModal = () => {
modal.show({component: LoginModalComponent, options: {name: 'LoginModal'}});
};
// Все что вы возвращаете вы теперь можете использовать в компонентах.
return {login, showLoginModal};
});
const {login, showLoginModal} = AuthPresenter(); // В компоненте или другом презентере
// Создание фабрики
import { createPresenterFactory } from '@azure-net/kit/edges';
import { createAsyncHelpers, createErrorParser } from '@azure-net/kit';
export const ErrorHandler = createErrorParser();
export const AsyncHelpers = createAsyncHelpers({ parseError: ErrorHandler }); // возвращает объект вида { createAsyncResource, createAsyncAction }
// Теперь используя для создания презентера AppPresenter вы сможете получать в каждом презентере доступ к AsyncHelpers и handleError
export const AppPresenter = createPresenterFactory({ ...AsyncHelpers, handleError: ErrorHandler });
// Полноценный пример презентера для crud на основе созданного с помощью фабрики презентера
// Описания парсера ошибок и AsyncHelpers есть в разделах ниже, также как и полное описание происходящего ниже
import { AppPresenter } from '$core/presenters';
import { ApplicationProvider } from '$web/application';
import { CreateScriptSchema, UpdateScriptSchema } from './schema';
import { refreshAsyncSignal } from '@azure-net/kit';
import { modalManager, notificationManager } from '$widgets';
import type { IScript, IScriptCollectionQuery, IScriptCreateRequest, IScriptUpdateRequest } from '$web/domain/script';
export const ScriptPresenter = AppPresenter('ScriptPresenter', ({ createAsyncResource, createAsyncAction }) => {
const { ScriptService } = ApplicationProvider();
const { show } = modalManager();
const { success: successNotification } = notificationManager();
const signalKeys = {
collection: 'ScriptCollectionSignal',
resource: 'ScriptResourceSignal'
};
const collection = async (query?: IScriptCollectionQuery) => await createAsyncResource(() => ScriptService.collection(query));
const resource = async (id: number) => await createAsyncResource(() => ScriptService.resource(id));
const create = async (request: Partial<IScriptCreateRequest>) =>
await createAsyncAction<IScript, IScriptCreateRequest>(() => ScriptService.create(CreateScriptSchema.from(request).json()), {
onSuccess: () => {
successNotification('shared.notifications.created');
}
});
const update = async (id: number, request: Partial<IScriptUpdateRequest>) =>
await createAsyncAction<IScript, IScriptUpdateRequest>(() => ScriptService.update(id, UpdateScriptSchema.from(request).json()), {
onSuccess: () => {
successNotification('shared.notifications.updated');
}
});
const remove = async (id: number) =>
await createAsyncAction(() => ScriptService.remove(id), {
onSuccess: () => {
successNotification('shared.notifications.deleted');
}
});
const showRemoveModal = async (resourceId: number) => {
show({
component: (await import('$components/shared/ConfirmModal/Component.svelte')).default,
props: {
onConfirm: async () => {
await remove(resourceId);
void refreshAsyncSignal(signalKeys.collection);
}
}
});
};
return { collection, resource, create, update, remove, signalKeys, showRemoveModal };
});