npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@apimediaru/dispatcher

v1.0.3

Published

Effect dispatcher package

Downloads

165

Readme

@apimediaru/dispatcher

Установка

npm install @apimediaru/dispatcher

Начало использования

Идея компонента: есть часто встречающиеся задачи, например, поменять заголовок в форме по клику, поменять подзаголовок, поставить новую цель, достигнуть какой-то цели и т.д. Ранее мы применяли императивный подход к решению таких задач, заводишь новый обработчик события на клик, создаешь функцию, в ней определяешь какие значения брать из каких атрибутов, что с этим значением делать и т.д. Получается много однотипного повторяющегося кода, который потом тяжело переиспользовать даже внутри одного проекта. Решено было как-то обобщить решение подобных микро задач в один компонент, чтоб его было легко использовать и настраивать, чтоб подход был максимально декларативным и не приходилось думать над тем как это сделать, а максимально сосредоточиться на том что нужно сделать. Для решения подобных задач был создан Dispatcher.

В качестве основы Dispatcher используются эффекты Effects - небольшие классы, создающиеся с помощью объекта-конфигурации. С помощью них можно быстро создать новый сценарий обработки нажатия на различные элементы. Условно можно представить эффект как запланированное действие, которое срабатывает при нажатии на элементы. Так как Dispatcher использует делегирование событий, то не имеет значения когда подходящий под эффект элемент был добавлен на страницу.

Эффект по своей сути состоит из трех составляющих, можно представить что такое эффект, ответив на вопросы:

  1. нажатие на какие элементы будет запускать этот эффект? trigger
  2. нужны ли эффекту какие-либо данные, если да, то откуда и как их получить при срабатывании эффекта? getter
  3. что нужно сделать с полученными данными? handler

В простейшем виде эффект будет выглядеть следующим образом:

import { createDispatcher, createEffect } from '@apimediaru/dispatcher';

// Создаем экземпляр диспетчера
const dispatcher = createDispatcher({
  // Опции диспетчера
  fallbackTarget: '.js-form-popup, #my-special-form',
});

// Создание эффекта
const effect = createEffect({
  // Эффект сработает по клику на элементы с классом .js-init-effect
  trigger: '.js-init-effect',
  // Значением эффекта всегда будет результат выполнения `getter`, в данном случае `value` элемента
  getter: ({ target }) => target.value,
  // Обработчик эффекта получит результат `getter` и в данном случае выведет значение в консоль
  handler: ({ value }) => console.log(value), 
});

// Добавляем эффект диспетчеру
dispatcher.addEffect(effect);

Однако, сам по себе эффект ничего не делает, чтобы эффект мог сработать, необходимо добавить его к диспетчеру эффектов Dispatcher, тогда он будет учитываться при обработке обытий.

Эффекты

DispatcherEffect

Эффект - сценарий обработки события нажатия на элемент, который позволяет декларативно описать на какие элементы должно обрабатываться нажатие, как получать данные и как этими данными оперировать. Существенно сокращает количество кода, необходимого для решения подобных задач и упрощает разработку (но это не точно).

Типы эффектов

Существует несколько встроенных сценариев эффектов, все они расширяют базовый класс DispatcherEffect и принимают в качестве аргумента объект с опциями.

| Тип | Класс | Factory | Назначение | |:------------|:-----------------------------|:-------------------------|:-------------------------------------------------------------------------| | pure | DispatcherEffect | createPureEffect | Пустой эффект, который требует явного указания getter и handler | | base | DispatcherEffectBase | createBaseEffect | Эффект с предустановленным getter, который берет значение из атрибутов | | code | DispatcherCodeEffect | createCodeEffect | Эффект для модификации содержимого DOM элементов | | input | DispatcherInputEffect | createInputEfffect | Эффект для модификации значения input элементов | | attribute | DispatcherAttributeEffect | createAttributeEffect | Эффект для изменения значения атрибутов элементов |

Можно получить список типов, импортировав объект EffectTypes:

import { EffectTypes } from '@apimediaru/dispatcher';

// EffectTypes.PURE;
// EffectTypes.BASE;
// EffectTypes.CODE;
// EffectTypes.INPUT;
// EffectTypes.ATTRIBUTE;

Общие опции для всех эффектов

interface EffectOptions {
  // CSS селектор, клик по таким элементам будет запускать эффект
  trigger: string;

  // Значение по умолчанию
  defaultValue?: string;

  // Цели для диспетчеризации эффекты по умолчанию
  to?: OverallSelector;

  // Атрибуты элемента, запустившем событие, которые будет проверять dispatcher для определения цели диспетчеризации эффекта
  toAttributes?: string;

  // Позволяет определить свою логику получения эффектом значения
  getter?(payload: { target: HTMLElement, event: DrivenEvent }): string;

  // Позволяет определить свою логику обработки эффекта
  handler?(payload: { value: any, dispatchTo: HTMLElement[], effect: DispathcerEffect }): void;

  // Коллбэк, срабатывает на каждую обработку эффекта
  onDispatch?: AnyFn;

  // Функция обратного вызова, которая вызывается перед обработкой эффекта.
  // Eсли функция явно вернет `false`, то выполнение эффекта будет остановлено.
  gate?(payload: { target: HTMLElement, event: DrivenEvent }): false | void;

  // Отменить действие по умолчанию для события
  preventDefault?: boolean;

  // Состояние включения эффекта. Выключенные эффекты игнорируются диспетчером
  disabled?: boolean;

  // У каждого типа эффектов могут быть свои дополнительные настройки
  settings?: any;
}

DispatcherEffect

DispatcherEffect создается с помощью функции createPureEffect(options: EffectCommonOptions = {}): DispatcherEffect.

Базовый эффект, реализация логики обработки которого полностью зависит от нужд разработчика, которому необходимо самостоятельно указать getter и handler.

Например, создадим простой эффект, который по нажатию на элементы с классом .js-demo, будет брать значение атрибута data-effect-value и выводить значение в консоль.

import { createPureEffect, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher().addEffect(
  createPureEffect({
    trigger: '.js-demo',
    getter: ({ element }) => element.getAtribute('data-effect-value'),
    handler: ({ value }) => console.log(value),
  }),
);

Могут быть ситуации, когда отсутствует необходимость получения значения из элемента, в таких случаях эффект может быть функциональным, для этого можно просто опустить определение getter.

import { createPureEffect, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher().addEffect(
  createPureEffect({
    trigger: '.js-demo',
    handler: () => alert('clicked'),
  }),
);

По умолчанию эффект, также как и события, всплывает наверх по DOM дереву, однако это поведение можно предотвратить с помощью опции gate. В качестве event используется класс-обертка DrivenEvent над браузерным событием, который позволяет отследить использование preventDefault, stopPropagation, stopImmediatePropagation. Доступ к оригинальному событию можно получить через event.originalEvent.

import { createPureEffect, createDispatcher } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();
dispatcher.addEffect(
  createPureEffect({
    trigger: '.js-demo',
    gate: ({ target, event }) => {
      if (target.classList.contains('no-bubble')) {
        event.stopPropagation();
      }
    },
    handler: () => alert('clicked'),
  }),
);

Также с помощью gate можно отменить поведение по умолчанию для события.

import { createPureEffect, createDispatcher } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();
dispatcher.addEffect(
  createPureEffect({
    trigger: '.js-demo',
    gate: ({ target, event }) => {
      if (target.tagName === 'A') {
        event.originalEvent.preventDefault();
      }
    },
    handler: () => alert('clicked'),
  }),
);

Либо остановить выполнение эффекта если функция gate явно вернет false.

import { createPureEffect, createDispatcher } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();
dispatcher.addEffect(
  createPureEffect({
    trigger: '.js-demo',
    gate: ({ target, event }) => {
      if (target.classList.contains('js-prevent-effect')) {
        return false;
      }
    },
    handler: () => alert('clicked'),
  }),
);

Все эффекты могут переопределять цели для диспетчеризации через атрибут data-dispatch-to путем указания CSS селектора в качестве значения. Так можно менять цели зффекта из разметки страницы.

<div class="js-default-effect-target"></div>

<!-- Поведение эффекта по умолчанию -->
<button class="js-demo" data-effect-value="demo">Trigger effect</button>

<!-- Диспетчеризация эффекта на другие элементы с помощью атрибута -->
<button class="js-demo" data-effect-value="demo" data-dispatch-to=".js-another-target, #my-custom-target">Trigger effect</button>

<!-- Дополнительные цели для диспетчеризации -->
<div class="js-another-target"></div>
<div id="my-custom-target"></div>
import { createPureEffect, createDispatcher } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher({
  fallbackTarget: '.js-default-effect-target',
});

dispatcher.addEffect(
  createPureEffect({
    trigger: '.js-demo',
    getter: ({ element }) => element.getAtribute('data-effect-value'),
    handler: ({ value, dispatchTo }) => {
      dispatchTo.forEach((element) => {
        element.setAttribute('data-new-attribute', value);
      });
    },
  }),
);

DispatcherEffectBase

Наследует класс DispatcherEffect.

DispatcherEffectBase создается с помощью функции createBaseEffect(options: BaseEffectOptions = {}): DispatcherEffectBase.

Более продвинутый класс, который спроектирован для того, чтобы брать значения из атрибутов или содержимого DOM элементов. Имеет встроенный getter, но по прежнему не имеет handler, который должен указываться самостоятельно.

Опции:

interface BaseEffectOptions extends EffectOptions {
  // Названия атрибутов для получения значения. Можно указать массив атрибутов,
  // тогда поиск значения будет происходить путем перебора массива атрибутов
  // до получения первого не пустого значения
  valueAttributes?: AttributeName;

  // Важно! Имеет больший приоритет чем опция `valueAttributes`!
  // Названия атрибутов, которые содержат css селектор. Сначала будет найдено первое не пустое значение
  // среди указанных атрибутов, затем содержимое первого найденно по селектору элемента элемента (innerHTML)
  // будет взято в качестве значения эффекта.
  // С помощью этой опции можно создавать скрытые элементы, которые будут использоваться для получения данных для
  // последующей обработки эффектом
  valueTemplateAttributes?: AttributeName;

  // В случаях, когда нужно провести манипуляции внутри целевых элементов, указывается css селектор. Тогда
  // цели для диспетчеризации будут искаться внутри `dispatchTo` элементов
  payloadTarget?: AttributeName;

Создадим для примера следующий эффект:

import { createBaseEffect, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher().addEffect(
  createBaseEffect({
    // Триггер эффекта
    trigger: '.js-demo',

    // Значение по умолчанию
    defaultValue: 'Стандартное значение',

    // Получение значения из атрибутов
    valueAttributes: ['data-first', 'data-second', 'data-third'],

    // Получение значения через содержимое DOM элементов
    valueTemplateAttributes: ['data-template', 'data-template-2'],

    // Обработчик значения
    handler: ({ value }) => console.log(valuee),
  }),
);
<!-- Получение значения из атрибутов -->
<!-- value -->
<button class="js-demo" data-first="value">Demo #1</button>
<!-- value 2 -->
<button class="js-demo" data-first data-second="value 2">Demo #2</button>
<!-- value 3 -->
<button class="js-demo" data-third="value 3">Demo #3</button>
<!-- Стандартное значение -->
<button class="js-demo" data-first data-second data-third>Demo #4</button>
<!-- Стандартное значение -->
<button class="js-demo">Demo #5</button>

<!-- Получение значения из DOM элементов -->
<div style="display: none;">
  <!-- Шаблоны значений -->
  <div id="template-1">Шаблонное значение</div>
  <div id="template-2">Значение-шаблон 2</div>
</div>
<!-- Шаблонное значение -->
<button class="js-demo" data-first="value" data-template="#template-1">Demo #6</button>
<!-- Значение-шаблон 2 -->
<button class="js-demo" data-template-2="template-2">Demo #6</button>
<!-- value 2 -->
<button class="js-demo" data-template-2 data-second="value 2">Demo #6</button>
<!-- Стандартное значение -->
<button class="js-demo" data-template-2 data-second>Demo #6</button>

DispatcherCodeEffect

Наследует класс DispatcherEffectBase.

DispatcherCodeEffect создается с помощью функции createCodeEffect(options: CodeEffectOptions = {}): DispatcherCodeEffect.

Данный эффект предназначен для изменения содержимого DOM элементов. Простейшие примеры использования: изменение заголовков всплывающих форм при нажатии на кнопку открытия, изменение подзаголовков и т.д. Данный эффект уже имеет свой стандартный handler, который, конечно, можно перепределить на свой через handler.

interface CodeEffectSettings {
  hideIfEmpty?: boolean | string;
}

interface CodeEffectOptions extends BaseEffectOptions {
  settings?: {
    // Скрывать элемент при пустых значениях (по умолчанию включено)
    // Если булевое значение, то скрывается сам элемент. Если строка - будет осуществлен поиск ближайшего родителя, если таковой найдется, то скрыт будет уже он
    hideIfEmpty?: boolean | string;
  };
}

Пример: необходимо по клику на элементы с классом js-demo-effect или дата-атрибутом data-demo-effect брать значение из атрибута data-first, если он пуст, то брать из data-second, затем найти на странице все элементы с классом js-data-target или id demo, внутри которых есть элементы с классом js-demo-title или js-title, поменять у таких элементов содержимое на знчение, полученное из атрибутов, но для одной кнопки все тоже самое нужно сделать для элемента с классом js-data-target-2. Если значение в атрибутах есть, то показывать элемент, если значения нет, то скрывать через display: none. Если у триггер-элемента заполнен атрибут data-source, то взять его значение, которое является селектором, и найти первый подходящий элемент на странице и взять в качестве значения содержимое этого элемента. Обычных подход к решению такой задачи потребует достаточно много кода. Решение через Dispatcher и DispatcherCodeEffect.

Разметка:

<div style="display: none;">
  <div id="hidden-source">
     <p>Hidden title template</p>
  </div>
</div>

<button class="js-demo-effect" data-first="example">Demo #1</button>
<button class="js-demo-effect" data-first="" data-second="example 2">Demo #2</button>
<button class="js-demo-effect" data-source="#hidden-source">Demo #3</button>
<button data-demo-effect data-second="example 3" data-dispatch-to=".js-data-target-2">Demo #4</button>

<div class="js-data-target">
  <h2 class="js-demo-title">Default title</h2>
</div>
<div id="demo">
  <h2 class="js-title">Default title #2</h2>
</div>
<div class="js-data-target-2">
  <h2 class="js-title">Default title #3</h2>
</div>

Создание эффекта:

import { createCodeEffect, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher().addEffect(
  createCodeEffect({
    // Триггером для срабатывания этого эффекта будут элементы, которые попадают под этот селектор
    trigger: '.js-demo-effect, [data-demo-effect]',
    // Бери значение сначала из атрибута `data-first`, затем из `data-second`
    valueAttributes: ['data-first', 'data-second'],
    // Шаблонное значение
    templateAttributes: 'data-source',
    // Селектор целевых элементов по умолчанию
    to: '.js-data-target, #demo',
    // Селектор для поиска элементов, где нужно поменять значение
    payloadTarget: '.js-demo-title, .js-title',
    // Логируем элемент и значение при каждом срабатывании эффекта
    onDispatch: ({ element, value }) => {
      console.log(element, value);
    },
    // Настройки для эффекта типа `CODE`
    settings: {
      // Скрывать элементы если значение не было найдено
      hideIfEmpty: true,
    },
  }),
);

DispatcherAttributeEffect

Наследует класс DispatcherEffectBase.

DispatcherAttributeEffect создается с помощью функции createCodeEffect(options: AttributeEffectOptions = {}): DispatcherAttributeEffect.

Эффект предназначен для изменения/установки значений атрибутов у целевых элементов. Например, изменений целей при открытии формы по клику на кнопку.

interface AttributeEffectOptions extends BaseEffectOptions {}

Пример: изменять атрибут data-form-goal у форм с классом js-form или js-form-2 при клике на элементы с классом js-set-form-goal. Брать значение из атрибутов data-goal, data-maybe-goal, data-definitely-goal, значение по умолчанию goal-default (demo #1,2,3,4). Также должна быть возможность поменять изменить цель в какой-то произвольной форме по селектору (demo #5).

Разметка:

<button class="js-set-form-goal" data-goal="goal">Demo #1</button>
<button class="js-set-form-goal" data-maybe-goal="goal 2">Demo #2</button>
<button class="js-set-form-goal" data-goal="" data-definitely-goal="goal 33">Demo #3</button>
<button class="js-set-form-goal">Demo #4</button>
<button class="js-set-form-goal" data-dispatch-to=".js-form-3" data-goal="custom target goal">Demo #5</button>

<form class="js-form"></form>
<form class="js-form-2"></form>

Эффект:

import { createAttributeEffect, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher().addEffect(
  createAttributeEffect({
    // Триггер
    trigger: '.js-set-form-goal',
    // Откуда брать значение
    valueAttributes: ['data-goal', 'data-maybe-goal', 'data-definitely-goal'],
    // Куда положить значение
    payloadAttributes: 'data-form-goal',
    // Значение по умолчанию
    defaultValue: 'goal-default',
    // С какими элементами работать
    to: '.js-form, .js-form-2',
  }),
);

DispatcherInputEffect

Наследует класс DispatcherInputEffect.

DispatcherAttributeEffect создается с помощью функции createInputEffect(options: AttributeEffectOptions = {}): DispatcherInputEffect.

Эффект предназначен для изменения значений input элементов с возможностью создать новый input элемент, если цель диспетчеризации не имеет подходящего элемента на момент срабатывания эффекта.

interface InputEffectSettings {
  createIfNotExists?: boolean | {
    attributes?: Attributes;
    properties?: Properties;
  };
}

interface InputEffectOptions extends BaseEffectOptions {
  settings?: InputEffectSettings;
}

Пример: необходимо поменять value у input элементов в форме, если таких нет, то создать скрытый readonly input c классом custom-input, типом text и присвоить значение. name input элементов - type. Совершать изменения по клику на элементы с классом js-set-input. Значение брать из атрибута data-new-value (#1), значение по умолчанию - default input value (#2). Искать среди элементов с классом js-input-parent.

<button class="js-set-input" data-new-value="input value">Demo #1</button>

<form class="js-input-parent">
  <input type="text" name="type">
</form>
<form class="js-input-parent"></form>
import { createAttributeEffect, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher().addEffect(
  createInputEffect({
    // Триггер
    trigger: '.js-set-input',
    // Откуда брать значения
    valueAttributes: 'data-new-value',
    // Значение по умолчанию
    defaultValue: 'default-input-value',
    // Где поменять значение
    payloadTarget: 'input[name="type"]',
    // С какими элементами работать по умолчанию
    to: '.js-input-parent',
    // Настройки для эффекта `INPUT`
    settings: {
      // Создать если не существует
      createIfNotExists: true,
      // Задать атрибуты
      attributes: {
        class: 'custom-input',
        type: 'text',
        name: 'type',
        readonly: '',
      },
      // Определить свойства
      properties: {
        hidden: true,
      },
    },
  }),
);

Создание эффектов на основе объектов

Для создания эффекта на основе объектов (может быть полезно при создании множества эффектов на основе объекта) необходимо воспользоваться функцией createEffect, сигнатура которой:

createEffect(options: Object = {}): typeof DispatcherEffect

Для указания какой эффект необходимо создать в объекте конфигурации необходимо указать тип эффекта с ключом effectType. В случаях когда effectType не указан, и также не указан getter, то будет создан DispatcherEffectBase, иначе DispatcherEffect.

import { createEffect, EffectTypes, createDispatcher } from '@apimediaru/dispatcher';

createDispatcher([
  // DispatcherEffect
  createEffect({
    // ... options for pure effect
  }),

  // DispatcherEffect
  createEffect({
    effectType: EffectTypes.PURE,
    // ... options for pure effect
  }),

  // DispatcherEffect
  createEffect({
    effectType: EffectTypes.BASE,
    // ... options for base effect
  }),

  // DispatcherEffect
  createEffect({
    effectType: EffectTypes.CODE,
    // ... options for code effect
  }),

  // DispatcherEffect
  createEffect({
    effectType: EffectTypes.INPUT,
    // ... options for input effect
  }),

  // DispatcherEffect
  createEffect({
    effectType: EffectTypes.ATTRIBUTE,
    // ... options for attribute effect
  }),
]);

С помощью такого способа создания эффектов можно создавать карты эффектов:

import { createDispatcher, addEffects, removeEffectsFromKernelDispatcher } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();

const effects = {
  title: {
    effectType: 'code',
    trigger: '.js-set-title',
    valueAttributes: 'data-attr',
    payloadTarget: '.js-title',
  },
  type: {
    effectType: 'input',
    trigger: '.js-set-type',
    valueAttributes: 'data-attr',
    payloadTarget: 'input[name=type]',
    settings: {
      createIfNotExists: false,
      attributes: {
        type: 'text',
        name: 'type',
      },
      properties: {
        hidden: true,
      },
    },
  },
  // ... прочие эффекты
};

// Добавляем эффекты на основе объекта
const dispatcherEffects = addEffects(dispatcher, effects);

// В дальнейшем мы можем отвязать эффекты от диспечтера если появится такая необходимость
removeEffects(dispatcher, dispatcherEffects);

Factory

Чтобы создать диспетчер, необходимо воспользоваться функцией createDispatcher, c сигнатурой createDispatcher(options: Object = {}): Dispatcher:

import { createDispatcher } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher({
  fallbackTarget: '.js-form-popup, #my-special-form',
});

Опции:

delegateTarget: string | HTMLElement | HTMLElement[]
Элементы к которым будет применяться делегирование событий.
По умолчанию: [document.documentElement]

fallbackTarget: null | string | HTMLElement | HTMLElement[]
Элементы, которые будут являться целями для диспетчеризации эффектов по умолчанию.
По умолчанию: null

Instance API

addEffect(effect: DispatcherEffect, id?: string): Dispatcher
Добавить новый эффект.

removeEffect(effect: string|DispatcherEffect): Dispatcher
Удалить существующий эффект по id или по ссылке на эффект.

getEffectId(effect: DispatcherEffect): string|null
Получить id эффекта.

getEffect(id: string): DispatcherEffect|null
Получить эффект по id.

getEffects(): DispatcherEffect[]
Получить массив всех добавленных эффектов.

getEffectEntries(): [string, DispatcherEffect][]
Возвращает массив пар эффектов [id, effect].

invokeGetter(effectId: string, element: HTMLElement): Promise<string|null>
Вызвать getter определенного эффекта и получить результирующее значение.

invokeOperation(effectId: string, payload: { value: any, dispatchTo: HTMLElement[] }): Dispatcher
Вызвать handler определенного эффекта.

Functional API

addEffects

addEffects(dispatcher: Dispatcher, namespaceOrEffects: string | DispatcherEffectList, rest?: DispatcherEffectList): DispatcherEffect[]

Для удобства разработчику предоставлена возможность добавлять эффекты в диспетчер несколькими способами в зависимости от ситуации.

Первым аргументом идет ссылка на диспетчер, второй аргумент может быть string - пространство имен или DispatcherEffectList, третий DispatcherEffectList если указано пространство имен.

type DispatcherEffectList = string | Array<DispatcherEffect | [string, DispathcerEffect]> | Object<string, DispatcherEffect>

Добавление эффектов представленных в виде массива, где каждый элемент массива может быть DispatcherEffect, либо кортежем [string, DispatcherEffect].

import { addEffects, createDispatcher, createEffect } from '@apimediaru/dispatcher';
const dispatcher = createDispatcher();
addEffects(dispatcher, [
  // id будет сгенерировано автоматически
  createEffect({
    // ... options
  }),
  
  // добавляем по выбранному id
  ['custom-effect', createEffect({
    // ... options
  })],
]);

Можно добавить эффекты в виде объекта, где ключ будет использоваться в качестве id эффекта. В случае, если значение не будет являться экземпляром класса DispatcherEffect, то эффект из такого объекта будет автоматически создан с помощью createEffect.

import { addEffects, createDispatcher, createEffect, EffectTypes } from '@apimediaru/dispatcher;';
const dispatcher = createDispatcher();
addEffects(dispatcher, 'custom-namespace', {
  // custom-namespace/first-effect
  'first-effect': createEffect({
    // ... options
  }),
  
  // custom-namespace/another-namespace
  'another-effect': {
    effectType: EffectTypes.CODE,
    trigger: '.js-inline-effect',
    getter: () => {},
    handler: () => {},
  },
});

removeEffects

removeEffects(dispatcher: Dispatcher, namespaceOrEffects: string | DispatcherEffectList, rest?: DispatcherEffectList): DispatcherEffect[]

Работает по такой же логике, как и addEffects, за исключением того, что можно передать в качестве аргумента с эффектами массив из строк идентификаторов.

import { addEffects, removeEffects, createDispatcher, createEffect, EffectTypes } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();
const effectsConfig = {
  first: createEffect(/* options */),
  second: createEffect(/* options */),
  third: createEffect(/* options */),
};

// Добавляем эффекты
const effects = addEffects(dispatcher, effectsConfig);

// Вариант 1: удалить эффект, передав массив из эффектов
removeEffects(dispatcher, effects);

// Вариант 2: удалить по id
removeEffects(dispatcher, ['first', 'second', 'third']);

Пример с импользованием пространства имен:

import { addEffects, removeEffects, createDispatcher, createEffect, EffectTypes } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();
const namespace = 'custom-namespace';
const effectsConfig = {
  first: createEffect(/* options */),
  second: createEffect(/* options */),
  third: createEffect(/* options */),
};

// Добавляем эффекты
const effects = addEffects(dispatcher, namespace, effectsConfig);

// Вариант 1: удалить эффект, передав массив из эффектов
removeEffects(dispatcher, effects);

// Вариант 2: удалить по id
removeEffects(dispatcher, namespace, ['first', 'second', 'third']);

Программный вызов эффектов

Существует возможность программного запуска эффектов через invokeGetter и invokeOperation.

Программный вызов эффекта

import { createDispatcher, createEffect, EffectTypes } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();

dispatcher.addEffect(createEffect({
  effectType: EffectTypes.PURE,
  trigger: '.js-demo',
  handler: () => alert('Invoke test'),
}), 'demo-effect');

// Будет вызван `alert`
dispatcher.invokeOperation('demo-effect');

Получение значения

import { createDispatcher, createEffect, EffectTypes } from '@apimediaru/dispatcher';

const dispatcher = createDispatcher();

dispatcher.addEffect(createEffect({
  effectType: 'pure',
  trigger: '.js-demo',
  getter: () => 'returned value',
}), 'demo-effect');

dispatcher.invokeGetter('demo-effect').then((value) => {
  console.log(value);
});

Более сложный пример с получением данных из элемента

const dispatcher = createDispatcher();

dispatcher.addEffect(createEffect({
  effectType: 'base',
  trigger: '.js-demo',
  valueAttributes: ['data-attr-1'],
}), 'demo-effect');

dispatcher.invokeGetter('demo-effect', document.getElementById('invoke')).then((value) => {
  console.log(value);
});