Question
· Apr 7, 2017

Auto select with ZEN dataCombo

Hi,

is there a way to implement the behaviour for a zen dataCombo? Let´s assume the dataCombo retrieves it´s data via sql query, lets say 50 records. Now the user type into the input of the datacombo 'a', which shall auto select the first entry beginning with character 'a'. If the type 'a' again, the next item from the list is selected. What I am looking for is an approach to implement such an behaviour. See a simple <select> component which support the desired functionality out-of the box.

I´ve found an old google group entry on this (https://groups.google.com/forum/#!searchin/intersystems-zen/datacombo%7C...), but it seems not to work here.

Any suggestion on this would be highly appreciated. Thank you.

best regards,

sebastian

Discussion (3)2
Log in or sign up to continue

Hi Sebastian,

In the past, when I've tried to be creative and make <dataCombo> work more like a <select>, the better solution has been to just use a <select>, possibly customized a little bit to make it behave nicer with changing query parameter values. What's the reasoning for using a <dataCombo> rather than a <select> in your case?

Here's some custom component code that might serve as a basis for a full solution. The trick is setting editable="true", overriding findSelectedItem to select the best match to the input text, and calling findSelectedItem after changes, using the built-in timer for the sake of simplicity. Compare to a normal dataCombo for reference.

/// dataCombo subclass with limited support for responding to keyboard events
Class DC.Demo.ZEN.Component.dataCombo Extends %ZEN.Component.dataCombo [ System = 3 ]
{

///  This is the XML namespace used for library components.
Parameter NAMESPACE = "http://www.intersystems.com/zen/dc/demo";

/// Always editable, of course.
Property editable As %ZEN.Datatype.boolean [ InitialExpression = 1 ];

/// Onclick, show the dropdown.
Property onclick As %ZEN.Datatype.eventHandler [ InitialExpression = "zenThis.showDropdown();" ];

/// Find and select item within the dropdown that matches current control value.
/// This is called when the dropdown appears to make sure that the current
/// item is highlighted.
ClientMethod findSelectedItem(force, update, select) [ Language = javascript ]
{
    force = ('undefined'!=force)?force:false;
    update = ('undefined'!=update)?update:true;
    select = ('undefined'!=select)?select:false;
    var inputValue = this.findElement('input').value.toUpperCase();
    this.keyMode = true;
    if (force||this.isDropdownVisible) {
        var count = this.getOptionCount();
        var dVal = new Array();
        for (var idx = 0; idx < count; idx++) {
            dVal[idx] = this.getOptionText(idx).toUpperCase();
            if ((inputValue <= dVal[idx])&&((idx == 0)||(inputValue > dVal[idx-1]))) {
                this.selectItem(idx,update,select);
                break;
            }
        }
    }
}

/// Start (or restart) timer normally used by "timer" mode (overridden to apply to all modes)
/// Users should not call this method.
ClientMethod startTimer() [ Internal, Language = javascript ]
{
    this.clearTimer();
    this.actionTimerId = self.setTimeout("zenPage.getComponent("+this.index+").timerHandler()",this.delay);
}

/// Clear timer normally used by "timer" mode (overridden to apply to all modes)
/// Users should not call this method.
ClientMethod clearTimer() [ Internal, Language = javascript ]
{
    if (this.actionTimerId) {
        self.clearTimeout(this.actionTimerId);
        this.actionTimerId = null;
    }
}

/// Timer event handler normally used by "timer" mode (overridden to apply to all modes)
/// Users should not call this method.
ClientMethod timerHandler() [ Internal, Language = javascript ]
{
    if (this.isDropdownVisible) {
        // refresh drop down only if searchKeyLen is not defined!
        if ((this.searchKeyLen != '') && (this.searchKeyLen > 0)) {
            this.renderDropdown();
        } else {
            this.findSelectedItem(); //Just find the selected item.
        }
    }
    else {
        this.showDropdown();
    }
}

/// Change handler for input control.
/// Users should not call this method.
ClientMethod inputChangeHandler() [ Internal, Language = javascript ]
{
    this.invokeSuper('inputChangeHandler');
    
    //If input was cleared, and the actual control value was changed, then clear the value and notify.
    var input = this.findElement('input');
    if (input.value == '') {
        if (this.getValue() != '') {
            this.setValue('');
            this.onchangeHandler();
        }
    }
}

/// Notification that this component is about to stop being modal.
ClientMethod onEndModalHandler(zindex) [ Language = javascript ]
{
    this.findSelectedItem(true,false,true);
    this.invokeSuper('onEndModalHandler',arguments);
}

}

In case you're unfamiliar with custom components: you can include this one in a Zen page like this:

<page xmlns="http://www.intersystems.com/zen" xmlns:demo="http://www.intersystems.com/zen/dc/demo">
<demo:dataCombo sql="select 'your query here'/>
</page>

Or like this:

<page xmlns="http://www.intersystems.com/zen" >
<dataCombo xmlns="http://www.intersystems.com/zen/dc/demo" sql="select 'your query here'" />
</page>

Hi Timothy,

there is in fact no reason to use the a dataCombo. A select will be fine, as long as it acts as a select. I thought about a custom component an your code is a very good starting point. I will review that and come back with my findings. Before I start, if the sql-select provides about 50 records I also need a functionality not only to select the item starting with a letter but also goto that position in the view (scroll down to the position, for example when 50 elements are found and the user types letter 'w'). This your approach cover that need or is there a method to overwrite?

Best regards,

sebastian