Page tree

Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.


  • The class gets instantiated with the property name to search for, which in this case will be "genre" inside the Track entity.
  • When it is time to create the query the appendCriteria call gets called. It:
    • Checks whether data was actually there. If not it returns the EMPTY indicator. This indicator is used when searching is only allowed with at least one clause filled in.
    • For complex data you would also check the input here and throw a ValidationException if the data was wrong.
    • We can now create the query: we add an or of all values in the set.

Generating the form

The search panel generates a form containing the labels and controls that are added. By default it generates a simple vertical form where each label/control pair are on a single line. How can we influence the form's layout? For that we need to know how the form gets generated.

The SearchPanel uses an ISearchFormBuilder implementation as the thing that actually builds the form:

Code Block
public interface ISearchFormBuilder {
   /** Defines the target node for the form to be built. */
   void setTarget(NodeContainer target) throws Exception;

   void append(SearchControlLine<?> it) throws Exception;

   void finish() throws Exception;

All actions that create the form based UI are delegated to this interface. If you do nothing then SearchPanel uses the default implementation of this thing: DefaultSearchFormBuilder. This implementation is very basic:

Code Block
public class DefaultSearchFormBuilder implements ISearchFormBuilder {
   private FormBuilder m_builder;

   private Div m_target;

   @Override public void setTarget(NodeContainer target) throws Exception {
      Div root = m_target = new Div("ui-dfsb-panel");
      Div d = new Div("ui-dfsb-part");
      m_builder = new FormBuilder(d);

   @Override public void append(SearchControlLine<?> it) throws Exception {
      NodeContainer label = it.getLabel();
      if(null != label)
      IControl<?> control = it.getControl();

   public void addBreak() {
      NodeContainer target = requireNonNull(m_target);
      Div d = new Div("ui-dfsb-part");
      m_builder = new FormBuilder(d);

   @Override public void finish() throws Exception {
      m_builder = null;

   public FormBuilder fb() {
      return requireNonNull(m_builder);

It just uses a normal FormBuilder to create the form, and it has an extra "method" to allow the form to be split into multiple columns: the "addBreak" method.

Controlling how the form looks can be done by creating your own implementation of ISearchFormBuilder. You can even make it the default layout by calling:

Code Block
SearchPanel.setDefaultSearchFormBuilder(Supplier<ISearchFormBuilder> factory)

so that the supplier returns your factory.

To interact with your factory you can add special "actions" to the SearchForm's definition. For example, to use that "addBreak()" method we would code the following:

Code Block
SearchPanel sp = new SearchPanel(Invoice.class);
DefaultSearchFormBuilder bld = new DefaultSearchFormBuilder();
sp.setFormBuilder(bld);									// Ensure that the DefaultFormBuilder is used

//-- add first two props
lf.add().property("type").control(typeC, new EnumSetQueryBuilder<>(Definition.pTYPE));
lf.add().action(() -> bld.addBreak());

The action will be executed in the order of adding the controls.


You could of course also cast lf.getFormBuilder() to DefaultSearchFormBuilder instead of creating an instance yourself. This however will cause your code to break if the default ever changes.