Как пробросить до UI URL к изображению, хранящемуся в FileStorage?

Добрый день.
Рассмотрим стандартный механизм отображения картинки из FileStorage

image

В HTML получаем URL данного изображения:
image

Я планирую на странице организовать кастомный просмотрщик изображений
Вроде такого:

У подобных просмотрщиков следующий API - необходимо сформировать массив URLов к изображениям.
image

Как мне подойти к данной задаче? Как я до JS могу докинуть URLы картинок в обход использования JMix компонента Image ?

Добрый день!

Предполагаю, что для интеграции Spotlight.js будет использоваться свой кастомный компонент, поэтому простенький пример будет основан на AbstractJavaScriptComponent.

Vaadin умеет добавлять ресурсы (иконки, изображения и т.п.) в общее состояние компонента, которое доступно и на клиентской части. Всё что нам нужно это добавить FileStorageResource в это состояние. Достигается это следующим образом:

DemoGallery.java
@JavaScript({
        "vaadin://demogallery/demo-gallery-connector.js"
})
public class DemoGallery extends AbstractJavaScriptComponent {

    protected KeyMapper<Resource> resourceKeyMapper = new KeyMapper<>();

    public void addFileStorageResource(FileStorageResource resource) {
        com.vaadin.server.Resource vResource = null;

        // получение ресурса с Ваадиновским типом
        if (resource != null && ((WebResource) resource).hasSource()) {
            vResource = ((WebResource) resource).getResource();
        }

        if (vResource != null) {
            String key = resourceKeyMapper.key(vResource);
            setResource(key, vResource); // установка ресрурса в состояние компонента
        }
    }
}

Таким образом в коннекторе компонента нам будут доступны корректные URI до переданных ресурсов. Дальше в JavaScript коннекторе мы должны получить реальные URL до ресурсов, которые сможем использовать.

demo-gallery-connector.js
com_company_demoresources_jscomponent_DemoGallery = function () {
    var connector =  this;
    var element = connector.getElement();

    element.innerHTML = '<div id="demo-gallery"></div>';

    function addImage(uri) {
        var img = document.createElement('img');
        var url = connector.translateVaadinUri(uri); // функция переводит URI в корректный URL
        img.src = url;

        img.style.width = "100px";
        img.style.height = "100px";
        element.append(img);
    }

    connector.onStateChange = function() {
        var state = connector.getState();

        if (!state.resources) {
            return;
        }
        // Есть ли ресурсы в коннекторе
        if (Object.keys(state.resources).length) {
            for (var resource in state.resources) {
                addImage(state.resources[resource].uRL);
            }
        }
    }
}

Добавление компонента на экран:

FileRefDescriptorBrowse.java
@Autowired
protected CollectionContainer<FileRefDescriptor> fileRefDescriptorsDc;
@Autowired
protected HBoxLayout demoGalleryBox;

@Subscribe
protected void onAfterShow(final AfterShowEvent event) {
    // Создание компонента
    DemoGallery demoGallery = new DemoGallery();
    demoGallery.setSizeFull();

    // Добавление FileRef в компонент
    for (FileRefDescriptor item : fileRefDescriptorsDc.getItems()) {
        FileStorageResource resource = getApplicationContext().getBean(FileStorageResource.class);
        resource.setFileReference(item.getFileRef());
        demoGallery.addFileStorageResource(resource);
    }
    
    // Добавление компонента в HBoxLayout
    demoGalleryBox.unwrap(AbstractOrderedLayout.class).addComponent(demoGallery);
}

Про JavaScriptComponent можно почитать здесь: Универсальный JavaScriptComponent :: Документация Jmix

Демо проект с примером: demo-resources.zip (96.7 КБ)

2 симпатии

Еще раз огромное спасибо за ответ и предоставленный пример!
Я только сейчас дошел до реализации - пример работает и все классно.

Я попытался повторить ровно тоже самое в своем проекте - прям взял Ваши файлы и перенес к себе.

Вместо галлереи получаю ошибку
image

Есть предположение, что я упустил?

Скорее всего проблема в том что вы не переименовали имя функции. В ней содержится и путь до коннектора. Я так понимаю DemoGallery.java у вас лежит в пакете nitel.documentationarchive.jsComponent. Cоответсвено в файле demo-gallery-connector.js нужно переименовать функцию com_company_demoresources_jscomponent_DemoGallery в nitel_documentationarchive_jsComponent_DemoGallery

1 симпатия

Спасибо! Именно в этом была проблема.

@yarik1706 , @pinyazhin , может подскажите, а как бороться с кешем в браузере?
JS файлы компонентов кешируются и их получается сбросить только в Developer Console

image.

Я пока при разработке только столкнулся… Как думаете, пользователи тоже будут сталкиваться с этим?

Во время разработки подобных компонентов я часто использую жесткую перезагрузку с очисткой кэша. Но на моей памяти пользователи ни разу не жаловались на то что с компонентами что-то не так (после их обновления и публикации новой версии). Если вы считаете это критичным. То скорее всего, как вариант, надо будет обновлять названия файла demo-gallery-connector.js (добавлять постфикс версии). Чтоб браузер грузил новую версию.

1 симпатия

@yarik1706 , @pinyazhin ,
Буду благодарен помощи в следующей задаче.

Я изначально в этой теме говорил, что достаточно в сторону JS компонента добросить список URL’ов. (на скриншоте ниже - gallery1).

Теперь задача усложнилась - помимо URLов к изображениям необходимо пробросить некоторую информацию. (на скриншоте ниже - gallery2). Необходимо иметь возможность модифировать данные.

image

Способ, которым поделился @pinyazhin способен только пробрасывать URLы картинок до компонента. Однако я не вижу как пробросить какие либо данные.
У AbstractJavaScriptComponent нет метода setState.

В итоге:

  1. Мне нужен JS компонент, который будет редактировать данные + отображать картинки.
  2. AbstractJavaScriptComponent отображает картинки, но я не понимаю как туда пробросить данные.
  3. JavaScriptComponent - умеет редактировать данные, но я не вижу как туда пробросить картинки.

По работе с AbstractJavaScriptComponent можно обратиться к документации кубы

https://doc.cuba-platform.com/manual-7.2-ru/js_library_sample.html

Там по моему различий с jmix никаких нету.

в общих чертах вы создаете класс наследник от JavaScriptComponentState с нужными вам параметрами.
в DemoGallery переопределяете методы getState чтоб они возвращали ваш класс наследованный от JavaScriptComponentState. В DemoGallery создаете сеттер на ваши данные в котором будете вызывать getState().setData(data)
setData здесь метод из “вашего” JavaScriptComponentState

2 симпатии

Спасибо! Я попробую.

Документация Cuba действительно помогла.
В очередной раз, огромное Вам спасибо :slight_smile:

А может подскажите как десериализовать JSON в Java объект?

Я из JS компонента в сторону Java собираюсь отправлять некий объект, состоящий из массива строк и примитивных полей.

image
На стороне Java я получаю JreJsonObject

image

Как его десериализовать в какой то соответствующий Java объект , построенный по классу ?

Насколько хорошие советы на StackOverFlow по этому поводу

image

Тут уже от вас зависит) Кому как удобнее)
ObjectMapper должен без проблем справится.

1 симпатия