EntitySuggestionField

EntitySuggestionField allows searching for entity instances according to a string entered by a user. It performs actions when a user clicks buttons on the right.

EntitySuggestionField is an input field with the additional functionality: a user enters a few characters, and if several matches have been found, a drop-down list will display all of them. EntitySuggestionField refreshes the list of options on each entered symbol.

The list of options is loaded in the background according to the logic defined by the application developer on the server-side.

In fact, EntitySuggestionField is a hybrid of SuggestionField and EntityPicker.

Component’s XML-name: entitySuggestionField.

Basics

Use EntitySuggestionField if:

  • Users need to select a single entity instance.

  • The number of options in the list of suggested values is too large to use EntityComboBox.

  • You want to provide high-performance search in the database without loading much data to the UI layer.

entity suggestion field

To create EntitySuggestionField connected to data, use the dataContainer and property attributes and the query element:

<data>
    <instance id="addressDc" class="ui.ex1.entity.Address"> (1)
        <fetchPlan extends="_base"> (2)
            <property name="country" fetchPlan="_instance_name"/>
        </fetchPlan>
    </instance>
</data>
<layout>
    <vbox spacing="true">
        <entitySuggestionField id="countryField"
                               inputPrompt="msg://entitySuggestionField/select"
                               caption="msg://ui.ex1.entity/Address.country"
                               dataContainer="addressDc"
                               property="country">
            <query entityClass="ui.ex1.entity.Country"
                   searchStringFormat="%${searchString}%"
                   escapeValueForLike="true"
                   fetchPlan="_instance_name">
                <![CDATA[select c from uiex1_Country c where lower(c.name) like lower(:searchString) escape '\']]>
            </query>
        </entitySuggestionField>
    </vbox>
</layout>
1 InstanceContainer for the Address entity.
2 Inline fetch plan of the entity instance located in the container.

The screen defines the addressDc data container for the Address entity having the country attribute in the example above. In the entitySuggestionField element, the dataContainer attribute contains a link to the addressDc data container, and the property attribute refers to the country entity attribute. The entity attribute should be a reference to an entity. In our example, it is Country.

Defining Options

You can specify a list of options either in the XML descriptor using the query nested element or programmatically in the controller using the SearchExecutor interface.

query

query is an optional element that enables defining a query for selecting suggested values.

The query element has the following attributes:

  • entityClass (required) - a full qualified name of an entity class.

  • fetchPlan - an optional attribute that specifies the fetch plan to be used for loading the queried entity.

  • escapeValueForLike - enables searching for the values that contain special symbols: %, \, etc. The default value is false.

  • searchStringFormat - a Groovy string; thus, you can use any valid Groovy-string expressions.

SearchExecutor

If query is not defined, you should programmatically set the list of options using SearchExecutor.

SearchExecutor is a functional interface that contains a single method: List<E> search(String searchString, Map<String, Object> searchParams).

Use the searchString parameter to filter candidates using the string entered by the user.

First, declare a component in the XML descriptor:

<entitySuggestionField id="entityField"
                       dataContainer="addressDc"
                       property="country"/>

Then set SearchExecutor to the component:

@Autowired
private DataManager dataManager;

@Install(to = "entityField", subject = "searchExecutor")
private List entityFieldSearchExecutor(String searchString, Map<String, Object> searchParams) {
    return dataManager.load(Country.class)
            .query("e.name like ?1 order by e.name", "(?i)%" + searchString + "%")
            .list();
}

You can generate the SearchExecutor implementation stub using Studio.

The search() method is executed in a background thread, so it cannot access visual components or data containers used by visual components. Call DataManager or a middleware service directly, or process and return data loaded to the screen beforehand.

You can use the escapeForLike() method to search for the values that contain special symbols:

@Install(to = "entitySuggestionField", subject = "searchExecutor")
private List entitySuggestionFieldSearchExecutor(String searchString, Map<String, Object> searchParams) {
    searchString = QueryUtils.escapeForLike(searchString);
    return dataManager.load(Customer.class)
            .query("e.firstName like ?1 escape '\\' order by e.firstName", "(?i)%" + searchString + "%")
            .list();
}

metaClass

You can use EntitySuggestionField without binding to the data container, that is, without setting dataContainer and property. In this case, use the metaClass attribute to specify an entity type for EntitySuggestionField. For example:

<entitySuggestionField id="metaClassField"
                       metaClass="uiex1_Country"/>

The search logic is defined using SearchExecutor:

@Install(to = "metaClassField", subject = "searchExecutor")
private List<Country> metaClassFieldSearchExecutor(String searchString, Map<String, Object> searchParams) {
    return dataManager.load(Country.class)
            .query("e.name like ?1 order by e.name", "(?i)%" + searchString + "%")
            .list();
}

Actions

You can define custom and predefined actions for EntitySuggestionField displayed as buttons on the right. You can do it either in the XML descriptor using the actions nested element or programmatically in the controller using the addAction() method.

Predefined Actions

If you create EntitySuggestionField without actions, the XML loader will define only the entity_lookup and entity_open actions. There are also the entity_clear and entity_openComposition actions.

To add other predefined action, for example, the entity_clear, you should specify the actions element as follows:

<entitySuggestionField dataContainer="addressDc"
                       property="country">
    <actions>
        <action id="lookup" type="entity_lookup"/>
        <action id="open" type="entity_open"/>
        <action id="clear" type="entity_clear"/>
    </actions>
</entitySuggestionField>

The actions element does not extend but overrides a set of standard actions. You should define the identifiers of all required actions explicitly.

Use addAction() to set actions programmatically. If the component is defined in the XML descriptor without actions nested element, it is sufficient to add missing actions:

@Autowired
private EntitySuggestionField<Country> entitySuggestionField;

@Autowired
private Actions actions;

@Subscribe
public void onInit(InitEvent event) {
    entitySuggestionField.addAction(actions.create(EntityClearAction.class));
}

Custom Actions

Custom actions for EntitySuggestionField are similar to custom actions for EntityPicker.

ArrowDownHandler Usage

ArrowDownEvent is fired when a user presses Arrow Down key when the popup with suggestions is hidden.

You can set up your own action handler and use EntitySuggestionField.showSuggestions() method, which accepts the list of entities to show suggestions:

@Autowired
private CollectionContainer<Country> countriesDc;

@Autowired
private EntitySuggestionField<Country> countryField;

@Install(to = "countryField", subject = "arrowDownHandler")
private void countryFieldArrowDownHandler(SuggestionFieldComponent.ArrowDownEvent arrowDownEvent) {
    countryField.showSuggestions(new ArrayList<>(countriesDc.getItems()));
}