views:

2810

answers:

7

I am using a dijit.form.FilteringSelect backed by a dojox.data.QueryReadStore in order to allow the user to select a region (think "auto-complete" mechanism). On each character entered by the user, the QueryReadStore sends a request to the server, awaiting a json list of matching regions (with associated IDs). By the time a short enough list is shown, the user picks the desired item. [Admittedly, querying on every keystroke is not the most efficient pattern, but it's good enough for now.]

Unexpected behaviour: in some rare but specific occasions, the choice made by the user "doesn't stick". For instance, if the user types "can", she is presented with the following choices in that order:

Atlantic Canada
Canada
English Canada
Lower Canada
Upper Canada
Western Canada

If she chooses "Canada" among these, the dijit closes the drop down selection, having correctly selected her choice. But by the time the user leaves the field, the selection switches to "Atlantic Canada"!

That bizarre phenomenon occurs systematically for a small number of specific regions. (At first, I thought that the common factor between those badly behaved regions was that their name contained accented characters or hyphens, but clearly not so with the Canadian example. So far, I fail to spot a regular pattern.)

I have not found any mention of a similar problem anywhere. I am quite willing to investigate, but, as I am new to dojo, I would really appreciate pointers before I resort to delving inside dojo's code: where should I first look? what are some likely problems that could cause that behaviour? can i rule out certain hypotheses? how should I best use the console (or Firebug) to get to the bottom of this? etc.

The problem occurs with both dojo 1.1.1 and dojo 1.2.3.

Here is the (programmatic) generation of the FilteringSelect:

new dijit.form.FilteringSelect({
   name = "region";
   autoComplete = false;
   hasDownArrow = false;
   labelAttr = "name";
   queryExpr = "${0}";
   store = new dojox.data.QueryReadStore({url:'/query/regions'});
}, myNode);

EDIT (2009/02/18): Additional details

Following damelin's answer, I wanted to understand what FilteringSelect saw of this situation. Suppose I connect logging functions to the FilteringSelect's events onChange and onBlur, I get the following play-by-play sequence:

  • I click in the field and type: can
  • drop-down list of 6 regions (listed above) appears
  • with the keyboard cursors, I move down the list to "Canada" (which is region with id 1)
  • I press Enter (thus selecting an item of the store). The drop-down list has by now disappeared and the text "Canada" appears in the field. At this point, the first event is fired, with the following logging:

    onChange event: region 1
    
  • I leave the field by pressing tab. Here, two events are fired one after the other, in the following order:

    onBlur event: region 1
    onChange event: region 246
    

(Region 246 is Atlantic Canada.) Now that's very interesting... By the time I leave the field (onBlur), Canada still is the selected value. The mysterious swap happens only after that...

A: 

I've got exactly the same problem. Did you find a solution for this?

damelin
I am glad to read that I am not completely deluded! Unfortunately, nothing so far from my end. (I am starting to think that I will have to write my own store.) I would love to hear your input as to the specific circumstances that trigger the problem: I still haven't figured it out...
pierdeux
I've updated the answer.
damelin
+2  A: 
damelin
Damelin, sorry for taking so long to come back to you. Unfortunately, I don't think your analysis reflects the situation I experience. I'll add details to the original question...
pierdeux
+1  A: 

I've had the same problem today, affecting both Dojo versions 1.1.1 and 1.2.0.

As far as I can figure out, the FilteringSelect does a final query after the user leaves the field and expects the query result to contain only one result, which it then uses for the value of the field.

It looks to me like a bug (although I hope I'm proven wrong), but one you might have to with for now.

This page has some (incomplete) information on it: http://www.nabble.com/Problems-with-QueryReadStore-td19269498.html.

Later edit:

If fact you get the same problem with a FilteringSelect regardless of the data store behind it, whenever there is are duplicate labels.

For example, after the widget loses focus it resets to the first option:

 <select id="coffee2" name="coffee2" 
    dojotype="dijit.form.FilteringSelect" 
    autoComplete="false">
    <script type="dojo/method" event="onChange"  args="newValue">
        console.log(dijit.byId('coffee2').getValue() + '/' + dijit.byId('coffee2').getDisplayedValue());
    </script>
    <option value="0">AAA</option>
    <option value="1">AAA</option>
    <option value="2">AAA</option>
    <option value="3">AAA</option>
    <option value="4">AAA</option>
  </select>

In my opinion more of a bug than a feature.

Thank you for the heads up!
pierdeux
A: 

Following pnt's comment...

This discussion makes clear where the problem lies: on "blur", the QueryReadStore re-queries for a last time the server, using the DisplayedValue as its query-string.

In my example, that would be "Canada". Given that the six regions in my shortlist (see further up) contain that string and that my server is programmed to return an alphabetical list of all the items containing the query string (and not merely those starting with it), the entire shortlist again is returned, and the FilteringSelect picks the first item among these --- which in this case is "Atlantic Canada".

If I am right in my analysis, one possible way forward would be for me to refactor my query service so as to put at the top of the list (before the alphabetical order) any item that matches exactly the query string.

pierdeux
A: 

We were having this same problem. We solved by modifying _setDisplayedValueAttr in FilteringSelect.js to just return immediately:

_setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){ return }

This is after breaking our heads on it for a day, this change is obviously quite evil, but it worked for us.

The underlying problem has something to do with this line in _callbackSetLabel:

this._setValueFromItem(result[0], priorityChange);

Which is exactly why your value is being set to the first one in the result list. I have no idea why this is this way.

Hope it worked out for you, we are using the trunk version of FilteringSelect.js by the way (http://trac.dojotoolkit.org/browser/dijit/trunk/form/FilteringSelect.js)

Marijn
A: 

"On each character entered by the user, the QueryReadStore sends a request to the server..."

There is the FilteringSelect attribute searchDelay that you can set, which waits the specified number of milliseconds before sending the request.

Simon
A: 

Damn, this particular problem was a real pain in my ass too!! And here is how I got rid of it, thanks to damelin answer.

In my case, the dijit.form.filteringSelect backed by a dojox.data.QueryReadStore was needed to be filled by a formatted string coming from multiple db values itself coming from complex tables (with some parent and even manyToMany relationship). Please don't ask why, just consider I love separating db tables as soon as possible, in order to avoid duplicates. All of that is in the context of a Zend Framework application, where the QueryReadStore is served by a controller action which I'll name here autocompletelistAction.

Therefore, starting from damelin answer, I began working on my autocompletelisteAction to separate the two cases of QueryReadStore request, reading the GET parameter and its eventual asterisk.

First, I clean the parameter and search for its last char :

$txt = (String) $this->_request->getParam('parameter');
$lastChar = substr($txt, -1);

Then, if the parameter has more than 1 char and no asterisk at the end, I'm gonna get rid of my $txt parameter and build my like clauses by hand :

if ((strlen($txt) >= 1) && ($lastChar != '*')) {
  // here, the parameter is the full text, which the user selected by
  // clicking on a shown element of the filteringSelect

  // Therefore, I "explode" the parameter to correspond to my searched values
  // and I build my SQL LIKE clauses without "%"
  // Consequence? There is only one result which is the good one.
}
else {
  // here, the parameter is a "part" of the search, which the user typed in

  // Therefore, I build my SQL LIKE clauses with "%$txt%"
}
// Here I can just launch my SQL queries with the built LIKE clauses
// and return result(s) to the QueryReadStore

Why checking if $txt is more than one char? Because within the Zend Framework, sometimes I can "autoload" the filteringSelect with the page, which sends a fully empty argument (even without asterisk), which then would go to my "explode" functions.

This complex scenario is mine, but I think each could adapt this simple PHP test on the parameter in order to serve the right answer.

So the real job was done by damelin here because he investigated the reasons through dojo elements.

BLackEYedBoy