FTS Search results opens editor screen by default #1492

Hi,

I would like to open a different screen when clicking on the search results from the FTS service. It now opens up the editor but I would like to open a ‘show’ screen instead. Can this be done (I guess it can) but how would I do this best?

Regards,
Berend Tel

Any thoughts on this one?

Hi Berend,

when you look at com.haulmont.fts.web.ui.results.SearchResultsWindow which is the page that lists the fts results, you see that there is an inner class InstanceClickListener which basically defines what happens when an item is clicked.
Normally it will use this code to open the editor:
openEditor(windowConfig.getEditorScreenId(metaClass), entity, openType);

So there is no general way do configure a different UI screen on that. In order to change the behavior, extend the search-results.xml and the Controller and use it in your application. You can then override the corresponding methods.

Generally the underlying question is how to break out of the editor / browse screen pattern. I myself find that fairly difficult to do so, because especially the concept of the editor is used in so many places in the platform. Take the ScreenHistory feature e.g. or the Breadcrumb. Since i often have the same situation that i want to have a show window, i changed my mind in that the default editor for the entity is the show window. In order to edit a particular entity, i create another screen and with that most of the problems disappear. Perhaps this is also a solution for you.

Bye
Mario

Hi Mario,

Thanks for your answer, very helpful. Although I’m not sure how to extend on the search-results.xml (and controller). Cuba studio does not provide this option so I guess I need to do it through the IDE but I’m not sure what to do there. Could you provide a couple of hints as well?

I do agree that the editor/browse pattern is somewhat limited. As you, I have a couple of ‘show’ windows but the majority is still editor/browse. So I will stick to the default being an editor still.
I think it would be a great idea of the Cuba pattern could be extended by providing a ‘show’ concept as well thou.

Regards, Berend

Actually you can extend the search results screen in Studio:

  • Select a package in the Web module on the Generic UI tab

  • Click New and select “Extend an existing screen” template

  • Enter a descriptor name, e.g. my-search-screen

  • Select search-results.xml : ftsSearchResults in the Extend screen field

The problem is that this screen’s controller is not quite extension-friendly, so you will have to copy-paste a lot of code in order to redefine the required part. So I’ve created an issue to simplify it in the future.

1 Like

Hi Konstantin,

Finally got to look at this again and I was able to create an extended screen definition for the search results. But I’m a bit puzzled what to do next.

From the original implementation in SearchResultsWindow.java I see that line 206 contains the call to open an editor which I would then need to alter. However, this is inside a protected class and it seems that to extend the code I need to copy everything as only the init can be overridden it seems. Am I correct or is there a more efficient way to do it?

You already mention that I would need to copy-and-paste a lot of code but I’m not sure if this means all code…

Hi Berend,

I’m afraid you have to copy-paste almost all until we make it more extension-friendly.

Hi Konstantin,

It seems that the code below is the minimal implementation I had to make to get things running. I would appreciate if you could take a quick look and check if this is indeed the case or that I’m doing things wrong.


package com.axxemble.base27.web.search;

import com.axxemble.base27.entity.Page;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.components.mainwindow.AppWorkArea;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.web.App;
import com.haulmont.cuba.web.toolkit.ui.CubaButton;
import com.haulmont.fts.global.SearchResult;
import com.haulmont.fts.web.ui.results.SearchResultsWindow;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.*;
import com.vaadin.ui.themes.BaseTheme;

import java.util.*;

public class SearchResults extends SearchResultsWindow {

    @Override
    public void init(Map<String, Object> params) {
        super.init(params);
    }

    @Override
    protected void displayInstances(String entityName, VerticalLayout instancesLayout) {
        List<SearchResult.Entry> entries = searchResult.getEntries(entityName);
//        Collections.sort(entries);

        for (SearchResult.Entry entry : entries) {
            HorizontalLayout instanceLayout = new HorizontalLayout();

            Button instanceBtn = new CubaButton(entry.getCaption());
            instanceBtn.setStyleName(BaseTheme.BUTTON_LINK);
            instanceBtn.addStyleName("fts-found-instance");
            instanceBtn.addClickListener(new InstanceClickListener(entityName, entry.getId()));

            instanceLayout.addComponent(instanceBtn);
            instanceLayout.setComponentAlignment(instanceBtn, com.vaadin.ui.Alignment.MIDDLE_LEFT);

            instancesLayout.addComponent(instanceLayout);

            SearchResult.HitInfo hi = searchResult.getHitInfo(entry.getId());
            if (hi != null) {
                List<String> list = new ArrayList<>(hi.getHits().size());
                for (Map.Entry<String, String> hitEntry : hi.getHits().entrySet()) {
                    String hitProperty = hitEntry.getKey();
                    list.add(service.getHitPropertyCaption(entityName, hitProperty) + ": " + hitEntry.getValue());
                }
                Collections.sort(list);

                for (String caption : list) {
                    HorizontalLayout hitLayout = new HorizontalLayout();
                    hitLayout.addStyleName("fts-hit");

                    Label hitLabel = new Label(caption);
                    hitLabel.setContentMode(ContentMode.HTML);
                    hitLabel.addStyleName("fts-hit");

                    hitLayout.addComponent(hitLabel);
                    hitLayout.setComponentAlignment(hitLabel, com.vaadin.ui.Alignment.MIDDLE_LEFT);

                    instancesLayout.addComponent(hitLayout);
                }
            }
        }
        if (!searchResult.getIds(entityName).isEmpty()) {
            displayMore(entityName, instancesLayout);
        }
    }

    protected class InstanceClickListener implements Button.ClickListener {

        protected String entityName;
        protected Object entityId;

        public InstanceClickListener(String entityName, Object entityId) {
            this.entityName = entityName;
            this.entityId = entityId;
        }

        @Override
        public void buttonClick(Button.ClickEvent event) {
            MetaClass metaClass = metadata.getSession().getClass(entityName);
            Entity entity = reloadEntity(metaClass, entityId);

            TopLevelWindow appWindow = App.getInstance().getTopLevelWindow();
            if (appWindow instanceof HasWorkArea) {
                AppWorkArea workArea = ((HasWorkArea) appWindow).getWorkArea();

                if (workArea != null) {
                    WindowManager.OpenType openType = AppWorkArea.Mode.TABBED == workArea.getMode() ?
                            WindowManager.OpenType.NEW_TAB : WindowManager.OpenType.THIS_TAB;

                    WindowConfig windowConfig = AppBeans.get(WindowConfig.NAME);

                    // If page, then do not open the editor but the show page instead
                    if (entity.getClass() == Page.class) {
                        // Get local data to fetch tag (not included in minimal)
                        entity = reloadEntityLocal(metaClass, entityId);
                        openWindow("base$Page.show", WindowManager.OpenType.THIS_TAB,
                                ParamsMap.of("Tag", ((Page) entity).getPageTag()));

                    } else {
                        // Else open editor window
                        openEditor(windowConfig.getEditorScreenId(metaClass), entity, openType);
                    }
                } else {
                    throw new IllegalStateException("Application does not have any configured work area");
                }
            }
        }
    }

    protected Entity reloadEntityLocal(MetaClass metaClass, Object entityId) {
        LoadContext lc = new LoadContext(metaClass);
        lc.setView(View.LOCAL);
        lc.setId(entityId);
        return getDsContext().getDataSupplier().load(lc);
    }
}

Probably it’s correct if it works for you. I would only recommend using DataManager.reload(entity, viewName) in your reloadEntityLocal() method.

The implementation from reloadEntityLocal() was actually taken from the original reloadEntity() implementation. Only change there was to use the View.Local which I needed to load required properties.

Anyway, thanks for confirming the correct approach.

Hello Konstantin,

Is it possible to display browser screens on fts-search instead of editor screens because in my project i only build browser screens.
Is the studio is extension-friendly now?

How to approach this?

Thanks in advance.

:ticket: See the following issue in our bug tracker:

https://youtrack.cuba-platform.com/issue/PL-8766