1

I am trying to do autocomplete when I type in characters in ice:selectInputText Issue I am facing is when I type in characters it brings even names which is not matching with the characters I type. See the below screen shot for reference.

a

Ideally autocomplete should display only the first row from the result, however it displays rows which are not matching my typed characters.

Only Abell Maryland 20606 should display.

This is the code which is using for the comparison, how can I modify this to suit to my requirement which is to display only those results which is matching to what I type.

public int compare(Object o1, Object o2) {
        if (o1 instanceof SelectItem) {
            s1 = ((SelectItem) o1).getLabel();
        } else {
            s1 = o1.toString();
        }

        if (o2 instanceof SelectItem) {
            s2 = ((SelectItem) o2).getLabel();
        } else {
            s2 = o2.toString();
        }            
        return s1.compareToIgnoreCase(s2);
    }
};

I am following this tutorial from Icefaces

http://wiki.icefaces.org/display/ICE/Auto-Complete

Update

My code in autocomplete.jspx

 <ice:selectInputText rows="10" width="300"
                        listVar="emp"
                        valueChangeListener="#{mybean.updateList}"
                        listValue="#{mybean.list}">
                         <f:facet name="selectInputText">
                   <ice:panelGrid columns="3" columnClasses="empNameCol">
                         <ice:outputText value="#{emp.empName}"/>                         
                   </ice:panelGrid>

method updateList

public void updateList(ValueChangeEvent event) {

    setMatches(event);

    if (event.getComponent() instanceof SelectInputText) {
        SelectInputText autoComplete = (SelectInputText)event.getComponent();
        if (autoComplete.getSelectedItem() != null) {
            bean = (Bean)autoComplete.getSelectedItem().getValue();
        }            
        else {
            Bean tempCity = getMatch(autoComplete.getValue().toString());
            if (tempCity != null) {
                bean = tempCity;
            }
        }
    }
}

Method setMatches

private void setMatches(ValueChangeEvent event) {

Object searchWord = event.getNewValue();
int maxMatches = ((SelectInputText)event.getComponent()).getRows();
List matchList = new ArrayList(maxMatches);

try {
    int insert = 
        Collections.binarySearch(dictionary, searchWord, AutoCompleteDictionary.LABEL_COMPARATOR);            
    if (insert < 0) {
        insert = Math.abs(insert) - 1;
    }

    for (int i = 0; i < maxMatches; i++) {                                
        if ((insert + i) >= dictionary.size() || i >= maxMatches) {
            break;
        }
        matchList.add(dictionary.get(insert + i));
    }
} catch (Throwable e) {
    e.printStackTrace();
    logger.error("Erorr finding autocomplete matches" + e.getMessage());
}        
if (this.matchesList != null) {
    this.matchesList.clear();
    this.matchesList = null;
}
this.matchesList = matchList;

}

Update 2

Modified setMatches method

 private void setMatches(ValueChangeEvent event) {
        Object searchWord = event.getNewValue();
        int maxMatches = ((SelectInputText) event.getComponent()).getRows();
        List matchList = new ArrayList(maxMatches);
         try {
             for(int i = 0; i < dictionary.size(); i++) {
                 SelectItem s = (SelectItem)dictionary.get(i);
                 if(s.getLabel().startsWith(searchWord.toString())) {
                     matchList.add(s);
                     if(matchList.size() == maxMatches)
                         break;
                 }   
             }
         } catch (Throwable e) {
             e.printStackTrace();
             logger.error("Erorr finding autocomplete matches" + e.getMessage());
         }        
         if (this.matchesList != null) {
             this.matchesList.clear();
             this.matchesList = null;
         }
         this.matchesList = matchList;
         }
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Jacob
  • 14,463
  • 65
  • 207
  • 320

1 Answers1

1

You have to update the list of SelectItems. Instead of just odering the list you have to filter the list (or creating a new one which only contains the matches). The next time the autocomplete-list renders it will evaluate the bound list again.

The tutorial of icefaces has some sources attached (bottom). Take a look at AutoCompleteBean . The method updateList(ValueChangeEvent e) calls setMatches(e). Within this method the list is assigned with a new one.

// assign new matchList
if (this.matchesList != null) {
   this.matchesList.clear();
   this.matchesList = null;
}
this.matchesList = matchList;

This causes the ui component to show only items which match the input.

To sum it up: ice:selectInputList will always show the items contained in its list, so reduce the items in the list to show the relevant ones only.

Regards

Update

private void setMatches(ValueChangeEvent event) {
Object searchWord = event.getNewValue();
int maxMatches = ((SelectInputText)event.getComponent()).getRows();
List matchList = new ArrayList(maxMatches);

try {
    for(int i = 0; i < dictionary.size(); i++) {
        SelectItem s = dictionary.get(i);
        if(s.getLabel().startsWith(searchWord)) {
            matchList.add(s);
            if(matchList.size() == maxMatches)
                break;
        }   
    }
} catch (Throwable e) {
    e.printStackTrace();
    logger.error("Erorr finding autocomplete matches" + e.getMessage());
}        
if (this.matchesList != null) {
    this.matchesList.clear();
    this.matchesList = null;
}
this.matchesList = matchList;
}

// note: not optimized, just to explain how to do.

Update 2 (short version)

/**
 * Fills the suggestionList with the given luceneResult.
 *
 * @param suggestionList                 The list to fill.
 * @param luceneResult                   The previously computed luceneResult.
 */
private static void fillLookupSuggestionList(final List<SelectItem> suggestionList,
    LuceneResult luceneResult)
{
    suggestionList.clear();

    String searchQuery = luceneResult.getLuceneResultConfig().getSearchQuery(); 
    if (luceneResult.getResultSize() <= 0)
    {
        suggestionList.add(new SelectItem(null, BundleHelper.i18n(LuceneLookupController.BUNDLE,
            LuceneLookupController.NO_ITEM_FOUND)));
    }
    else
    {
        List<LuceneResultEntry> results = luceneResult.getResult();
        for (LuceneResultEntry entry : results)
        {
            suggestionList.add(new SelectItem(entry.getMetaInfo(),
                entry.getInfo().getDescription()));
        }
    }
}
Dennis Bayer
  • 109
  • 5
  • I have a doubt in this, I am already using this bit of code in the updatelist. `// assign new matchList if (this.matchesList != null) { this.matchesList.clear(); this.matchesList = null; } this.matchesList = matchList;` Then why I am not able to reduce the items contained in the list? Sorry to ask this question as I didn't understand your point. – Jacob Jan 28 '12 at 18:26
  • 1
    @Polappan Hi. I also use this component. Everytime the user enters something I start a Lucene search which returns the results. Everytime a search is started I have to create a new list list of SelectItems. This temporary list is assigned to the bean's member which is bound to the component. At this point it is hard for me to understand how you reduce your list's item. The code above shows the sorting only. Could you provide some code showing how reducing (and re-assigning) is done? – Dennis Bayer Jan 30 '12 at 07:53
  • Thanks for your help. I have added my code as update to my original post above. My code is almost same as the tutorial code except for the source of data. – Jacob Jan 30 '12 at 10:23
  • 1
    @Polappan Ok, there we have it. The example is not the best one. The binary search finds the index for the given searchWord only. Afterwards the example fills the new list with the items starting at this index until the maximum of the list is reached - not bothering if it matches the input. Try something like the updated example above. Regards – Dennis Bayer Jan 30 '12 at 11:12
  • Thanks again for the help. However after modifying setMatches method, I couldn't get any results for any words. I have added my modified method above as update 2. Regards – Jacob Jan 30 '12 at 11:41
  • Hm, very strange. Honestly, I don't see the fault. Reducing or creating a new list containing the matching results only is definitely the right way to do (as I have done it by myself). The only suggestion at the moment is to debug and see why the list is not filled. Sorry mate. – Dennis Bayer Jan 30 '12 at 12:07
  • The only difference to your code is I have modified like the following `SelectItem s = (SelectItem)dictionary.get(i); if(s.getLabel().startsWith(searchWord.toString())) {` I will try to debug though. Thanks again. – Jacob Jan 30 '12 at 12:19
  • Another question, do you mind sharing your code for auto completion if you don't mind? – Jacob Jan 30 '12 at 12:30
  • I resolved the issue, the code which you provided as update 1 was fine. It was my silly mistake. After debugging I found that the issue was with case. So I modified this line as `if(s.getLabel().toLowerCase() .startsWith(searchWord.toString().toLowerCase())) { ` and that resolved the issue. Thanks a lot for your help. Appreciated. – Jacob Jan 31 '12 at 05:15
  • I am facing a small hiccup with autocomplete list that my autocomplete list when it is displayed inside a popuppanel is not getting displayed correctly, it is getting displayed in the right bottom corner. Any idea how can I resolve this issue? I have posted this as a different thread [here](http://stackoverflow.com/questions/9095103/autocomplete-list-not-aligned-properly-under-selectinputtext) – Jacob Feb 02 '12 at 05:09