Вот как обрабатывать перетаскивание и любые другие события!

По большей части вы будете обходиться теми событиями, которые уже встроены в Elm. Но если вы программируете в Elm достаточно долго — а почему бы вам и не захотеть — вам нужно будет записывать свои собственные события. Но документация по пользовательским событиям скудна. Эта статья предназначена для того, чтобы исправить эту ошибку и помочь вам писать собственные события с апломбом! С апломбом говорю!

Четыре функции

В модуле Html.Events есть четыре функции, которые можно использовать для определения пользовательского обработчика событий в Elm. Вот их подписи:

Они различаются тем, предотвращают ли действие по умолчанию или останавливают распространение события:

  1. on Не может остановить распространение и не может предотвратить действие по умолчанию.
  2. stopPropagationOn Может остановить распространение события, но не может предотвратить действие по умолчанию.
  3. preventDefaultOn Не может остановить распространение события, но может предотвратить действие по умолчанию.
  4. custom Может остановить распространение события, а также может предотвратить действие по умолчанию.

Функция включения

Вот снова сигнатура функции on:

  • Первый аргумент: бит String достаточно прост — для события клика введите щелчок; для сенсорных событий введите touchstart, touchmove, touchend; и так далее. Суть в том, что нам просто нужно ввести имя события, которое мы использовали бы в Javascript. (У MDN есть полный список событий здесь.)
  • Второй аргумент. Пользовательская функция ожидает декодер в качестве второго аргумента. Но что будет декодировать декодер? Elm предоставит ему объект, соответствующий событию, которое мы здесь перехватываем. Мы бы написали это на Javascript:

Вы видите, что e передается обработчику событий? Это то, что здесь будет анализировать декодер. Какова структура этого объекта e? Это то же самое, что и в Javascript.

Если вам не нужны данные в этом объекте, нет необходимости их декодировать. Просто используйте Json.Decode.succeed и получите msg. Вот так:

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

В Elm вы бы написали декодер так:

Декодер используется в пользовательском событии следующим образом:

И вот как это пользовательское событие можно использовать в приложении:

Другими словами: когда значение ползунка диапазона изменяется, свойство valueAsNumber объекта события будет анализироваться с использованием свойства vanDecoder. Полученное значение будет заключено в сообщение GotAValue и отправлено. Мы обрабатываем это сообщение, как обычно, в разделе update приложения. Все хорошо?

Остальные три функции

Теперь, когда вы освоили основы, у вас не возникнет проблем с использованием трех других функций. Они отличаются от функции on только вторым аргументом: вместо просто сообщения ожидается конфигурация в виде кортежа или записи.

Опять же, самый простой сценарий — это когда вы не хотите декодировать какие-либо данные. Тогда код будет выглядеть так:

Если нам действительно нужны данные в объекте события, то решение приведено ниже:

Вот что делает приведенный выше код:

  1. eventObjectDecoder — это декодер, который мы пишем для извлечения данных из объекта события.
  2. Вызов Decode.map msg eventObjectDecoder в строке 7 приведет к выводу в виде Decoder msg. Но функция stopPropagationOn ожидает, что ее второй аргумент будет Decoder (msg, Bool). Таким образом, мы не можем просто соединить Decoder msg со значением True и протолкнуть его через Decode.succeed, как мы делали ранее, потому что мы просто получим Decoder (Decoder msg, Bool).
  3. Что нам нужно сделать, так это каким-то образом преобразовать Decoder msg в msg. Именно это и делают функции map. И, поскольку мы работаем в области декодирования, нам нужно использовать map функций в модуле Decode.
  4. Глядя на документацию, нам больше всего подходит Decode.map2. Он принимает два значения Decoder и направляет их через функцию, чтобы получить одно значение Decoder value.
  5. Итак, вместо того, чтобы просто передавать значение True, мы используем aDecode.succeed True.
  6. Наконец, мы отправляем Tuple.pair, Decoder msg из строки 7 и Decode.succeed True из строки 8 через функцию Decode.map2, чтобы получить вывод в виде Decoder (msg, Bool).

(Да, это апостериорное объяснение; не очень легко прийти к этому с помощью одних только рассуждений. Но, надеюсь, объяснение поможет шагам и концепциям придерживаться.)

Функция preventDefaultOn работает аналогично stopPropagationOn.

Однако функция custom требует небольшого изменения. Вот его подпись:

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

И вот как пользовательское событие определяется с помощью функции custom:

Уникальность приведенного выше кода заключается в том, что вместо обычной функции Decode.map3 объединяет выходные данные трех декодеров, используя конструктор псевдонима типа. Аккуратно, да?!

Обработка перетаскивания файлов

Приведенный ниже код объединяет все концепции, которые мы обсуждали выше. В приведенном ниже коде показано, как файлы можно перетаскивать в приложение.

Примечание. Я обратился к этой странице, чтобы узнать структуру объекта события перетаскивания.

Вы могли заметить, что пользовательское событие onDrop ничего не делает; он передает сообщение NoOp и ничего не делает в разделе update кода. Причина, по которой он вообще вставлен, заключается в том, что есть причуда — если вы не определите его, операция перетаскивания не будет работать.

Заключение

Вы можете создавать собственные события в Elm и перехватывать любые события, которые вы можете использовать с помощью JavaScript. Небольшое препятствие для написания пользовательских событий в основном связано с тем, как извлекаются и используются данные в объекте события. Кроме того, это легко-легко, не так ли?

Это все для этой статьи. Если у вас есть какие-либо конкретные темы, о которых вы хотели бы, чтобы я написал, не стесняйтесь оставлять комментарии. Ваше здоровье!