Вот как обрабатывать перетаскивание и любые другие события!
По большей части вы будете обходиться теми событиями, которые уже встроены в Elm. Но если вы программируете в Elm достаточно долго — а почему бы вам и не захотеть — вам нужно будет записывать свои собственные события. Но документация по пользовательским событиям скудна. Эта статья предназначена для того, чтобы исправить эту ошибку и помочь вам писать собственные события с апломбом! С апломбом говорю!
Четыре функции
В модуле Html.Events
есть четыре функции, которые можно использовать для определения пользовательского обработчика событий в Elm. Вот их подписи:
Они различаются тем, предотвращают ли действие по умолчанию или останавливают распространение события:
on
Не может остановить распространение и не может предотвратить действие по умолчанию.stopPropagationOn
Может остановить распространение события, но не может предотвратить действие по умолчанию.preventDefaultOn
Не может остановить распространение события, но может предотвратить действие по умолчанию.custom
Может остановить распространение события, а также может предотвратить действие по умолчанию.
Функция включения
Вот снова сигнатура функции on
:
- Первый аргумент: бит
String
достаточно прост — для события клика введите щелчок; для сенсорных событий введите touchstart, touchmove, touchend; и так далее. Суть в том, что нам просто нужно ввести имя события, которое мы использовали бы в Javascript. (У MDN есть полный список событий здесь.) - Второй аргумент. Пользовательская функция ожидает декодер в качестве второго аргумента. Но что будет декодировать декодер? Elm предоставит ему объект, соответствующий событию, которое мы здесь перехватываем. Мы бы написали это на Javascript:
Вы видите, что e
передается обработчику событий? Это то, что здесь будет анализировать декодер. Какова структура этого объекта e
? Это то же самое, что и в Javascript.
Если вам не нужны данные в этом объекте, нет необходимости их декодировать. Просто используйте Json.Decode.succeed
и получите msg
. Вот так:
Однако если вам действительно нужны данные, вам придется написать декодер. Предположим, мы хотим получить числовое значение ползунка диапазона. В Javascript вы получите это, выполнив следующее:
В Elm вы бы написали декодер так:
Декодер используется в пользовательском событии следующим образом:
И вот как это пользовательское событие можно использовать в приложении:
Другими словами: когда значение ползунка диапазона изменяется, свойство valueAsNumber
объекта события будет анализироваться с использованием свойства vanDecoder
. Полученное значение будет заключено в сообщение GotAValue
и отправлено. Мы обрабатываем это сообщение, как обычно, в разделе update
приложения. Все хорошо?
Остальные три функции
Теперь, когда вы освоили основы, у вас не возникнет проблем с использованием трех других функций. Они отличаются от функции on
только вторым аргументом: вместо просто сообщения ожидается конфигурация в виде кортежа или записи.
Опять же, самый простой сценарий — это когда вы не хотите декодировать какие-либо данные. Тогда код будет выглядеть так:
Если нам действительно нужны данные в объекте события, то решение приведено ниже:
Вот что делает приведенный выше код:
eventObjectDecoder
— это декодер, который мы пишем для извлечения данных из объекта события.- Вызов
Decode.map msg eventObjectDecoder
в строке 7 приведет к выводу в видеDecoder msg
. Но функцияstopPropagationOn
ожидает, что ее второй аргумент будетDecoder (msg, Bool)
. Таким образом, мы не можем просто соединитьDecoder msg
со значениемTrue
и протолкнуть его черезDecode.succeed
, как мы делали ранее, потому что мы просто получимDecoder (Decoder msg, Bool)
. - Что нам нужно сделать, так это каким-то образом преобразовать
Decoder msg
вmsg
. Именно это и делают функцииmap
. И, поскольку мы работаем в области декодирования, нам нужно использоватьmap
функций в модулеDecode
. - Глядя на документацию, нам больше всего подходит
Decode.map2
. Он принимает два значенияDecoder
и направляет их через функцию, чтобы получить одно значениеDecoder value
. - Итак, вместо того, чтобы просто передавать значение
True
, мы используем aDecode.succeed True
. - Наконец, мы отправляем
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. Небольшое препятствие для написания пользовательских событий в основном связано с тем, как извлекаются и используются данные в объекте события. Кроме того, это легко-легко, не так ли?
Это все для этой статьи. Если у вас есть какие-либо конкретные темы, о которых вы хотели бы, чтобы я написал, не стесняйтесь оставлять комментарии. Ваше здоровье!