«Сделать невозможное возможным» или как разработчики Creative боролись за асинхронность в Quill
Всем привет! Меня зовут Андрей, я frontend-разработчик компании Creative. Сегодня хочу поделиться с вами довольно интересным кейсом. Однажды заказчик обратился к нам с довольно нетривиальной задачей – создать в редакторе Quill Rich Text Editor блок, который будет появляться при вставке ссылки, отправлять запросы серверу и «доставать» описание, тайтл, превью и иконку страницы. Вроде ничего сложного, но мы столкнулись с двумя большими НО. Об этом и будет моя статья. Поехали!
Если вы уже работали с Quill Rich Text Editor, то наслышаны о его весьма «подробной» документации. Если нет, поясню. Дело в том, что в доках Quill’a чётко не описано, как в нём создавать свои кастомные блоки.
Это оказалось первой проблемой, с которой мы столкнулись. На поиски решения ушло полтора рабочих дня и литр слёз разработчика, но в конечном счёте добились желаемого (иллюстрация 1).
Второй проблемой стала невозможность внедрения нашей функции для получения html-кода страницы, из которого мы сможем забрать нужные нам meta-теги. Выглядит она примерно так (иллюстрация 2).
Какое решение этой проблемы приходит в голову первым? Отправить асинхронный запрос на сервер и работать с полученным ответом. Мы это сделали и… ничего не получилось. После провала с простым async/await провели эксперимент и сделали самовызывающуюся функцию (иллюстрация 3).
Выше я обратил ваше внимание на том, что static всегда должен возвращать node. Но при асинхронности именно этого и не происходит: код переходит к выполнению другого метода и закономерно выдаёт нам с вами ошибку о том, что ему не с чем работать. Почему так происходит? Потому что он не получил элемент.
Перелопатив кучу статей по поводу того, как Quill может работать с асинхронностью внутри своих модулей, я пришёл к выводу, что работать с ней он не может в принципе. Единственным вариантом является подготовка всех данных заранее во внешнем модуле (родителе), т.е. при вызове вставки этого модуля мы сами прописываем всю логику (иллюстрация 4).
После этих манипуляций при создании блока мы сможем получить все данные, обращаясь к объекту data (иллюстрация 5).
Итак, данные успешно получены, сохранены и теперь при следующей отрисовке они будут либо загружаться как новые, либо забираться обратно с помощью data-атрибутов.
Весь кастомный контент Quill’a я советую собирать именно с помощью атрибутов. Это позволит сохранять контент так, как удобно вам, а не редактору :) (иллюстрация 6).
В конце этой статьи хочу кратко обозначить две самые распространённые ошибки при работе с Quill Rich Text Editor. Надеюсь, эта информация позволит вам сохранить и время, и нервы:
- Не стоит создавать блоки, в которых есть эмодзи или другие иконочные шрифты – при редактировании текста на них тоже накладываются изменения.
- Если вы хотите изменять контент кастомного блока из компонента (в нашем случае это был Vue-компонент), вам нужно прокинуть кастомное событие с уникальным идентификатором блока. P.S.: в случае с React.js все гораздо проще, и можно просто вызвать функцию рендер с нужным нам компонентом. Но всё-таки, имейте этот вариант под рукой)
На этом у меня всё, буду рад ответить на ваши вопросы в комментариях!