Как после привычного 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.