Pull to refresh

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

Level of difficultyMedium
Reading time7 min
Views6.6K

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

Tags:
Hubs:
Total votes 8: ↑7 and ↓1+7
Comments2

Articles