Configuration Dialog for Optional Input Port

I am trying to build a NodeDialog for an optional input data port, but when the data port is not attached, the configuration fails and I get an “Error loading model settings”. I can’t get my NodeDialog to not load Dialog Components when they are not required. How is that done?

A longer description of what I am trying to do…

I’m building a node that is designed to manipulate data in only a subset of rows. Instead of re-inventing the ‘Row Splitter’ node, my node is designed to sit directly downstream of either a Row Splitter or Row Filter. My NodeDialog has configuration options for Port 0 (required) and similar configuration options for Port 1 (optional). My two configuration panels called “Top Input” and “Bottom Input” allow the user to define the data manipulation they want. The idea is that you can manipulate both halves of your data and then glue it all back together again in a single node.

I am using a combination of DialogComponentColumnFilter2 and DialogComponentColumnNameSelection components in both the “Top Input” and “Bottom Input” panels. The user selects the multiple columns they want manipulated, then the individual columns that do the manipulation. Both DialogComponents need to be told which Input Data Port they ought to point to (0 or 1), but as Port 1 is optional and null I’ve been getting errors.

My first attempted fix was to add a loadAdditionalSettingsFrom function to check if the optional data was available. My thought was to re-route all of the DialogComponents to Port 0 if Port 1 was not available (and then just ignore it). But I discovered that loadAdditionalSettingsFrom isn’t even triggered unless both Port 0 and Port 1 are connected!

I’m stuck. Ideally I would like the “Bottom Input” configuration panel to completely disappear if there is no Port 1 data. But at this stage I’d be happy with any solution that blocked the error message from appearing.

For DialogComponentColumnNameSelection, you need to use one of the constructors with the isRequired parameter, e.g.

/**
     * Constructor that puts label and combobox into the panel.
     *
     * @param model the model holding the value of this component. If the model
     * is an instance of {@link SettingsModelColumnName} a RowID option is
     * added to the select list.
     * @param label label for dialog in front of checkbox
     * @param specIndex index of (input) port listing available columns
     * @param isRequired true, if the component should throw an exception in
     *            case of no available compatible column, false otherwise.
     * @param classFilter which classes are available for selection
    */
    public DialogComponentColumnNameSelection(final SettingsModelString model,
            final String label, final int specIndex, final boolean isRequired,
            final Class<? extends DataValue>... classFilter) {
        this(model, label, specIndex, isRequired,
                new DataValueColumnFilter(classFilter));
    }

and set that value to false for the optional port. Then you can handle disabling the associated settings model in the #loadAdditionalSettings() method.

I’m not sure whether there is a solution for the column filter, but maybe fixing the column chooser will solve everything!

Steve

1 Like

Thanks for the suggestion Steve!

Yes - I forgot to mention I had already tried that. The solution was mentioned in another very helpful conversation you were part of:

https://forum.knime.com/t/dialog-refuses-to-open-need-input-table-spec-to-configure-dialog

But as you observe, I think it is the DialogComponentColumnFilter2 that is the real problem. It offers very few constructors and no isRequired parameter.

I just wanted to close out this discussion for those who are interested in learning what happened. The solution worked out perfectly fine for me, but unfortunately this solution will probably not satisfy anybody else stuck in a similar situation.

The source of my solution can be found in my original description of the problem:

“My node is designed to sit directly downstream of a Row Splitter … the idea is that you can manipulate both halves of your data and then glue it all back together again in a single node.”

So both my ports should be getting data from the same source, and my configuration for Port 1 (optional) can therefore use the same table specification as Port 0 (required). This doesn’t satisfy every use case, but will work 99% of the time.

This is what the NodeDialog code looks like (notice the ‘Bottom’ and ‘Top’):

DialogComponentColumnFilter2 dialogBottomColumnFilter = new DialogComponentColumnFilter2(createBottomColumnFilter(), INPUT_PORT_TOP);

But there is also a small gotcha in the NodeModel as the same logic needs to be repeated there (even after checking if the bottom port has been connected). This is what the NodeModel code looks like:

if( inData[INPUT_PORT_BOTTOM] != null ) {
	Collections.addAll( setColumns, m_columnsBottom.applyTo( inData[INPUT_PORT_TOP].getDataTableSpec() ).getIncludes() );
}

You must continue to use the Port 0 table specification to check the Port 1 data. The reason this caught me by surprise is that the applyTo() and getIncludes() will otherwise mark all bottom columns as included whether they were selected or not (when the table specifications are different).

1 Like

Thanks @Edlueze - that’s definitely a limited use case workaround! There are a couple of other gotcha’s with the DialogComponentColumnFilter2 panel (see e.g.

and

), which @gab1one was going to look into - maybe this optional behaviour might also be added to the list…

Steve

1 Like

In my opinion DialogComponentColumnFilter2 is in severe need of a rework, I struggle with it regularly myself…

best,
Gabriel

2 Likes