Java на стероидах, или опыт работы с Jmix

от автора

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

Привет! Меня зовут Никита, я Java‑разработчик в Red Collar. Так вышло, что я стал первопроходцем платформы Jmix и в короткие сроки сделал на ней уже два проекта. Кажется, мне даже понравилось ? Делюсь своим опытом и мыслями, почему вообще решил познакомиться с платформой. Также кратко расскажу об этапах работы над проектами, который реализовали на Jmix.

Коротко о Jmix

Jmix — это высокоуровневая Java-платформа на базе открытого кода для быстрого создания корпоративных информационных систем. Платформа Jmix — преемник другой известной платформы, CUBA Platform. Поставляется с расширенными инструментами и обширным набором функциональных модулей.

Система позволяет создавать как серверную часть, так и пользовательский интерфейс приложения. Подходит для разработки приложений со сложной моделью данных и широким пользовательским интерфейсом.

В чем суть Jmix и почему Java-разработчику проще адаптироваться

Jmix основан на Spring Boot, который де-факто является стандартом для создания корпоративных веб-приложений Java. Это означает, что разработчик может использовать множество сторонних библиотек и фреймворков с минимальной конфигурацией в дополнение к функциям, предоставляемым Jmix. Джависту будет несложно освоить платформу — здесь есть подробная документация в двух языках, которая поможет разобраться на старте. 

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

Jmix Studio — что это такое

Отдельного внимания заслуживает компонент платформы Jmix Studio, который является плагином для IntelliJ IDEA. Studio реализует визуальные инструменты для разработки приложения и помогает на всех этапах:

  • создание и настройка проекта;

  • определение модели данных;

  • создание сценариев миграции базы данных; 

  • разработка экранов пользовательского интерфейса в визуальном редакторе. 

Модуль обеспечивает расширенную навигацию, автозавершение кода и проверки, специфичные для проектов Jmix. Одновременно с разработкой кода в стандартном «текстовом» режиме разработчик может полноценно использовать визуальные редакторы экранных форм, бизнес-процессов и иной инструментарий для ускорения разработки проекта.

Мы обратили внимание на платформу, потому что она ускоряет работу, позволяет быстро создавать UI-компоненты и навешивать на них информацию. Вместе с Java-коллегой начали факультативно знакомиться с Jmix. В процессе коллега поняла, что она старовер и ей ближе Spring, а я продолжил изучать Jmix — «Java на стероидах», получается. 

Вскоре к нам пришел клиент с запросом оперативно сделать промо-сайт с большим количеством анимации, 3D-моделей и графики, а также с удобной админкой для управления каталогом, вакансиями и формами обратной связи. Решили попробовать сделать с Jmix админку, которую обычно пишем на Laravel и на Bitrix. 

На реализацию ушел месяц. Функциональность сайта должна была включать:

  • Каталог товаров с фильтрами, анимацией.

  • Раздел с вакансиями.

  • Формы обратной связи для клиентов и партнеров.

  • Административную панель с гибкой системой ролей для управления всеми разделами сайта.

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

В рамках нашего проекта админкой могут пользоваться несколько человек — со стороны и нас, и клиента. Сделали несколько ролей в админке. 1 пользователь мог редактировать товары, но не мог редактировать раздел с вакансиями. 2 пользователь, наоборот, редактирует вакансии, но товары не затрагивает.

Второй проект на Jmix: ошибки, шишки и решения

Между первым и вторым — перерыв оказался небольшим, и я сразу занялся следующим схожим проектом. Здесь подробнее расскажу об этапах работы над геймдев-сайтом, реализованном с Jmix.

На первый взгляд казалось, что проект будет очень похожим на первый — после него я уже многое проработал, набрал шишек, которые уже прошли, но…



Реализация



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

Помучил вопросами нашего PHP-разработчика, придумал базовую структуру и приступил к реализации.

 В основе лежит интерфейс Block, у которого есть свойство type — все блоки будут наследоваться от этого интерфейса, определять свой тип и уже в наследнике хранить нужные блоку данные.

public interface ContentBlock {      @JSONPropertyIgnore     BlockType getBlockType(); }
public enum BlockType {     RICH_TEXT_AREA("richTextAreaBlock"),     PRIVACY_TERMS("privacyTermsBlock"),     CODE("codeBlock"),     QUOTE("quoteBlock"),     IMAGE_WITH_CAPTION("imageWithCaptionBlock"),     GAME_SCREENSHOT("gameScreenshot"),     GAME_FEATURE("gameFeature"),     ABOUT_VALUE("aboutValue"),     ABOUT_PERK("aboutPerk"),     GAME("game");      private String type;      BlockType(String type) {         this.type = type;     }      public String getType() {         return type;     } }



Далее для каждого блока создавался визуальный компонент для удобного заполнения. В самом компоненте каждый блок являлся своеобразным стейтом для сохранения или для отображения при редактировании сущностей. 
Каждому визуальному компоненту автоматически добавлялись кнопки для изменения их порядка в родителе.

public class RichTextAreaBlock implements Serializable, ContentBlock {       private final String type = getBlockType().getType();      private String label;     private String content;      @Override     @JSONPropertyIgnore     public BlockType getBlockType() {         return BlockType.RICH_TEXT_AREA;     } }
@UiController("RichTextAreaFragment") @UiDescriptor("rich-text-area-fragment.xml") public class RichTextAreaFragment extends ScreenFragment {      private RichTextAreaBlock state; //    ...... }



Для хранения использовал тип JsonObject, который впоследствии мапился в Java класс. Что мы получаем в итоге: например, возьмем Новость —
 у нас есть блок, а у блока есть несколько наследников. В админке с помощью кнопок создаем нужные блоки — они отображаются в контейнере. С помощью кнопок, встроенных в каждый компонент, мы можем менять блоки местами.

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



Что получилось:

JavaScript Component

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


Подумал о RichTextArea из коробки Jmix, но его функциональности не хватило. 


Здесь в историю с двух ног врывается JavaScript Component
 — компонент пользовательского интерфейса, который может работать с любой оболочкой JavaScript без реализации компонента Vaadin. Таким образом, можно интегрировать любой чистый компонент JavaScript в свое приложение на основе Jmix.



Работает это таким образом:

  • Объявляем его в XML-дескрипторе экрана. 

  • Прописываем нужные зависимости на локальные файлы, стили, так и обычные зависимости (с рядом ограничений).


Далее создаем коннектор — код на JS.

Сonnector JS
ui_ex1_components_javascript_RichTextEditor = function () {     let connector = this;     let element = connector.getElement();     element.innerHTML = `           <div id="${connector.getState().data.options.editorId}"/>       `;       connector.onStateChange = function () {         let state = connector.getState();         let data = state.data;          let toolbarOptions = {                 container: [                     [{header: [2, 3, 4, false]}],                     ["bold"],                     ["underline"],                     [{list: "ordered"}, {list: "bullet"}],                     [                         {align: ""},                         {align: "center"},                         {align: "right"},                         {align: "justify"},                     ],                     ["link"],                     [{direction: "rtl"}],                     ["clean"],                  ],              };          let formats = ["link", "list", "header", "bold","underline", "direction", "align"];            let quill = new Quill(`#${connector.getState().data.options.editorId}`, {             theme: "snow",             formats: formats,             modules: {                 toolbar: toolbarOptions,               },         });           quill.on('text-change', function (delta, oldDelta, source) {             if (source === 'user') {                 connector.valueChanged(quill.getText(),                     quill.getContents(),                     quillGetHTML(quill.getContents()));             }         });          connector.setValue = function (value) {             quill.setContents(htmlToDelta(value))             quill.update();         }      }      function quillGetHTML(inputDelta) {         var tempCont = document.createElement("div");         (new Quill(tempCont)).setContents(inputDelta);         return tempCont.getElementsByClassName("ql-editor")[0].innerHTML;     }      function htmlToDelta(html) {         const div = document.createElement('div');         div.setAttribute('id', 'htmlToDelta');         div.innerHTML = `<div id="quillEditor" style="display:none">${html}</div>`;         document.body.appendChild(div);          let formats = ["link", "list", "header", "bold", "underline", "align", "direction"];         let toolbarOptions = [             [{header: [2, 3, 4, false]}],             ["bold"],             [{list: "ordered"}, {list: "bullet"}],             [                 {align: ""},                 {align: "center"},                 {align: "right"},                 {align: "justify"},             ],             [{direction: "rtl"}],             ["link"],             ["clean"], // remove formatting button         ];            const quill = new Quill('#quillEditor', {             theme: 'snow',             formats: formats,             modules: {                 toolbar: toolbarOptions,               },         });         const delta = quill.getContents();         document.getElementById('htmlToDelta').remove();         return delta;     }   };


В Java-реализации экрана создаем переменную компонента. Если надо, добавляем функции, которые можно вызвать из коннектора. Саму переменную можно использовать, чтобы вызывать функции внутри JS-файла.



На один из популярных редакторов текста был мануал в документах Jmix. Это редактор Quill, и он подошел. Все минимальные функции для задач он поддерживал. Я мог спокойно поменять местами или убрать кнопки из тулбара, которые мне не нужны.

Итоги: с чего начать изучение Jmix и стоит ли начинать

По факту, в обучении ничего сверхъестественного нет. Джависту будет легко ворваться — пролистать документацию, сделать компонент и свериться с докой. Отличие в том, что помимо работы с данными, здесь оперируем с UI-компонентами, при этом уверенных знаний как у фронта в верстке здесь не надо — все на XML.

Хорошо работает с миграцией, не нужно писать вручную. Например, для сущности у нас был определенный набор полей. Если клиент решит заменить одно поле другим, не нужно писать миграцию. Убираем в коде одно поле, добавляем другое, прописываем тип, настраиваем связи. При запуске проекта автоматически происходит миграция. 

Подводя небольшие итоги, проговорю, что с Jmix в кратчайшие сроки сделали и внедрили структуру, которая в базовом виде пытается быть похожей на то, что происходит в Bitrix и Laravel.


ссылка на оригинал статьи https://habr.com/ru/articles/728896/