13 Replies Latest reply: Feb 24, 2012 6:22 AM by Immo Benjes RSS

How To: Check and modify the values from h:inputTexts when t

Konstantin Spirov Newbie

This thread is created to find a sollution for the issues, raised in the comments of https://jira.jboss.org/jira/browse/RF-7527. Let's summarize briefly:

Imagine you liked the following example from RichFaces Demo -RichInput:SuggestionBox:ObjectsUsageExample.

Now you want to add such "combobox-like" component in and application, where a field is validated (or changed) each time, when the focus goes to some other field. Example: seam-gen generated application.

See an example source code:

<h:form id="form">
<a4j:queue/>
<a4j:outputPanel id="XXXXX">
<h:panelGrid columns="2">
<h:outputText value="Input with states names suggestions"/>
<h:panelGrid columns="2" border="0" cellpadding="0" cellspacing="0">
<h:inputText style="margin:0px;" id="statesinput" value="#{userBean.name}">
<a4j:support event="onblur" reRender="XXXXX" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
<h:graphicImage value="/images/icons/arrow.png"
onclick="#{rich:component('suggestion')}.callSuggestion(true)"
alt=""/>
</h:panelGrid>
</h:panelGrid>
<rich:suggestionbox height="200" width="200"
suggestionAction="#{capitalsBean.autocomplete}" var="cap"
for="statesinput" fetchValue="#{cap.name}" id="suggestion" tokens=",">
<h:column>
<h:graphicImage value="#{cap.stateFlag}"/>
</h:column>
<h:column>
<h:outputText value="#{cap.name}"/>
</h:column>
</rich:suggestionbox>
</a4j:outputPanel>
</h:form>

Unfortunately, this implementation (the only obvious) doesn't run flawlessly, see: http://www.youtube.com/watch?v=3lQR5iAv_8k

In the comments, connected with the issue, Mr. Shaikovsky gave explanation why this happens. But now the question is what to do.

I's obvoius, that we need solution that will allow us:

1) To validate the field from the server each time the focus is lost
2) To correct(change) the field from the server (and to make user able to see the changes)
3) This (1 and 2) should work
A) for fields manually entered (without using suggestionbox)
B) For fields entered from suggestion box (permited navigation: with keyboard or with mouse).

Otherwise this approach with suggestion box will be usable only with applications, where the validation is made with "submit" button - we are limiting the scope of this good component.

So let's think what to do.

  • 1. HOWTO: inputText with  suggestionbox when focus is lost
    Konstantin Spirov Newbie

    My subject is croped, it was

    How To: Check and modify the values from h:inputTexts when the focus is lost if rich:suggestionbox is used

    Moderators, please change the subject to:

    HOWTO: inputText with suggestionbox when focus is lost

  • 2. Re: How To: Check and modify the values from h:inputTexts wh
    Ilya Shaikovsky Master

     

    <h:form id="form">
     <a4j:queue requestDelay="300"/>
     <script type="text/javascript">
     var suggestionRequestFired=false;
     function myFunc(){
     if (suggestionRequestFired) {
     callAjax();
     }
     suggestionRequestFired = true;
     }
     </script>
     <a4j:outputPanel id="XXXXX">
     <h:panelGrid columns="2">
     <h:outputText value="Input with states names suggestions" />
     <h:panelGrid columns="2" border="0" cellpadding="0" cellspacing="0">
     <h:inputText style="margin:0px;" id="statesinput"
     value="#{userBean.name}" onblur="setTimeout('myFunc()',300);">
     </h:inputText>
     <h:graphicImage value="/images/icons/arrow.png"
     onclick="suggestionRequestFired=false; #{rich:component('suggestion')}.callSuggestion(true); #{rich:element('statesinput')}.focus();"
     alt="" />
     </h:panelGrid>
     </h:panelGrid>
     <rich:suggestionbox onsubmit="suggestionRequestFired=true;" height="200" width="200" onselect="if (event.type!='keydown') suggestionRequestFired=false;"
     suggestionAction="#{capitalsBean.autocomplete}" var="cap"
     for="statesinput" fetchValue="#{cap.name}" id="suggestion"
     tokens=",">
     <h:column>
     <h:graphicImage value="#{cap.stateFlag}" />
     </h:column>
     <h:column>
     <h:outputText value="#{cap.name}" />
     </h:column>
     </rich:suggestionbox>
     </a4j:outputPanel>
     <a4j:jsFunction name="callAjax" ajaxSingle="true" process="statesinput" reRender="XXXXX"></a4j:jsFunction>
     </h:form>
    


    unfortunatelly need such workarounds because input is not a part of suggestion components itself and knows nothing if the blur was fired on selection or on actual blur.



  • 3. Re: How To: Check and modify the values from h:inputTexts wh
    Ilya Shaikovsky Master

    maybe we need to revise this and implement the suggestion with built-in input in 4.x in order to handle the events in context of single component.

  • 4. Re: How To: Check and modify the values from h:inputTexts wh
    Konstantin Spirov Newbie

    Two days later - no any visible flaws - works OK! Our QA for sure will like the change in the app stability - it is very notable. More intensive testing upcoming. I am thinking how to refactor this for forms, where there are a lot of fields like this (the code had grown too mach). When I have results, I will write more ASAP.

    P.S. It tricky and not trivial, but the important is, that at least now we have
    something, that works well enough. It is OK for me, but certainly something must be done in the future to make this easier.

    Thank you very much.

  • 5. Re: How To: Check and modify the values from h:inputTexts wh
    Ilya Shaikovsky Master

    Thanks again for you update with the results! And yes.. my previous comment was just about to changing the approach for this component in future :)

  • 6. Re: How To: Check and modify the values from h:inputTexts wh
    Jay Balunas Master

     

    "ilya_shaikovsky" wrote:
    maybe we need to revise this and implement the suggestion with built-in input in 4.x in order to handle the events in context of single component.


    I like this idea - especially since 80% of suggestions box use will be on input component. Please create a feature request jira for the 4.0 timeframe.

  • 7. Re: How To: Check and modify the values from h:inputTexts wh
    Konstantin Spirov Newbie

    Update 2 (not the final)

    Some changes were made to your example code:

    Change 1: We put parameter to the function, in order to reuse the JavaScript (of course it is not a good idea to put different function and flags for all fileds - in one of our documents we have more then 20 suggestion boxes).

    Change 2: There was the following problem in your example - when the user clicks on the arrow three times ( 1. show 2. hide 3. show), the suggestion box shown at step 3 stays a few moments and disappears.
    The reason is that the field is already focused.

    The solution of this bug was in the same spirit as in your example: to make sure, that suggestionRequestFired = true; from onsubmit was called "at the end of the party".

    So our example becomes:

    
    <h:form id="form">
     <a4j:queue requestDelay="300"/>
     <script type="text/javascript">
     var suggestionRequestFired=true;
     function leaveCriticalSection() {
     setTimeout('suggestionRequestFired=true;', 600);
     }
     function enterCriticalSection() {
     suggestionRequestFired = false;
     }
    
     function myAjax(param){
     setTimeout('if (suggestionRequestFired) {'+param+'();} suggestionRequestFired = true;', 300);
     }
     </script>
     <a4j:outputPanel id="XXXXX">
     <h:panelGrid columns="2">
     <h:outputText value="Input with states names suggestions" />
     <h:panelGrid columns="2" border="0" cellpadding="0" cellspacing="0">
     <h:inputText style="margin:0px;" id="statesinput"
     value="#{userBean.name}" onblur="myAjax('XXXXX_AJAX');">
     </h:inputText>
     <h:graphicImage value="/images/icons/arrow.png"
     onclick="enterCriticalSection(); #{rich:component('suggestion')}.callSuggestion(true); #{rich:element('
    statesinput')}.focus();"
     alt="" />
     </h:panelGrid>
     </h:panelGrid>
     <rich:suggestionbox
     height="200" width="200"
     onsubmit="leaveCriticalSection();"
     onselect="if (event.type!='keydown') enterCriticalSection();"
     suggestionAction="#{capitalsBean.autocomplete}" var="cap"
     for="statesinput" fetchValue="#{cap.name}" id="suggestion"
     tokens=",">
     <h:column>
     <h:graphicImage value="#{cap.stateFlag}" />
     </h:column>
     <h:column>
     <h:outputText value="#{cap.name}" />
     </h:column>
     </rich:suggestionbox>
     </a4j:outputPanel>
     <a4j:jsFunction name="XXXXX_AJAX" ajaxSingle="true" process="statesinput" reRender="XXXXX"></a4j:jsFunction>
     </h:form>
    


    Today we will do some tests on server, which is too away (the ping is big). I will send new update later.

  • 9. Re: How To: Check and modify the values from h:inputTexts wh
    Konstantin Spirov Newbie

    Update 3:

    On system with big response, when the arrow is clicked too fast, the conflict happened from time to time.

    Probably this can become always repeatable if you add delay at the backing bean, but I cannot focus on this now.

    Here is a modification of the previous code that prevents this (a bit rude, but enough).

    
    function enterCriticalSection() {
     if (!suggestionRequestFired) return false;
     suggestionRequestFired = false;
     return true;
    }
    ...
    onclick="if (!enterCriticalSection()) return false; #{rich:component ...
    




  • 10. Re: How To: Check and modify the values from h:inputTexts wh
    Konstantin Spirov Newbie

    I hadn't forgotten to send a final report.

    Here is the solution we are using. It works very stable with the current (daily snapshots) of RichFaces (there were some differences!). The best for us is, that it is OK for documents with great complexity

    Notes:
    1) I don't call anything on onSubmit (as you will see below - not needed)
    2) There are only 3 JS functions and no other JS code (myAjax(), onGuggestionArrow(), onSuggestionSelect())
    3) The convention in our templates for ID of the components is: XXXXX_FLD, XXXXX_AJAX, XXXXX, XXXXX_SUG

     <s:decorate id="ITM_HSC_COD_FLD" template="layout/sedit.xhtml">
     <a:jsFunction name="ITM_HSC_COD_AJAX" ajaxSingle="true" process="ITM_HSC_COD"
     reRender="ITM_HSC_COD_FLD, ITM_HSC_NAM_FLD"></a:jsFunction>
     <h:panelGrid columns="2" border="0" cellpadding="0" cellspacing="0">
     <h:inputText id="ITM_HSC_COD"
     size="22"
     maxlength="22"
     value="#{itmHistoBeans.tarCode}"
     required="true"
     onblur="myAjax('ITM_HSC_COD_AJAX')"
     style="width:133px;height:17px">
     </h:inputText>
     <h:graphicImage value="/img/arrow.png"
     onclick="onSuggestionArrow('ITM_HSC_COD')"
     alt="" style="position:relative; left:-20px;"/>
     </h:panelGrid>
     <rich:suggestionbox id="ITM_HSC_COD_SUG" tokens=","
     for="ITM_HSC_COD"
     onselect="onSuggestionSelect();"
     suggestionAction="#{refList.autocompleteTar}"
     shadowOpacity="4"
     border="1"
     width="500"
     height="200"
     shadowDepth="4"
     cellpadding="2"
     nothingLabel="Not found"
     var="result"
     zindex="25000"
     fetchValue="#{result.hscKey}"
    
     >
     <h:column>
     <h:outputText value="#{result.fulKey} "/>
     </h:column>
     <h:column>
     <h:outputText value="#{result.tarDsc}"/>
     </h:column>
     </rich:suggestionbox>
     </s:decorate>
    


    Here is the js we created and reuse

    Notes:
    1) Of coure you can eliminate getFirstElementById if you use name instead of ID
    2) I think it is simpler - at the moments we don't need rerendering, we just cancel it. This is universal and covers many situations (including the unpredicted :-)

    
    // rerender pending
    var reRender = null;
    
    String.prototype.endsWith = function(str)
    {return (this.match(str+"$")==str)}
    
    function getFirstElementById(id) {
     var all = document.getElementsByTagName("*");
     for (var i = 0; i < all.length; i++)
     {
     if (all.id.endsWith(':'+id))
     {
     return all;
     }
     }
     return null;
    }
    
    
    function terminateRendering() {
     if (reRender== null) return;
     // we will be here when selection is done by mouse
     // redundand rerender in the queue - it would work even with it, but the focus would be lost
     clearTimeout(reRender);
     reRender=null;
    }
    
    
    function onSuggestionSelect() {
     terminateRendering();
    }
    
    
    function onSuggestionArrow(param) {
     terminateRendering();
     var sug_comp = getFirstElementById(param);
     sug_comp.focus();
     var suge = getFirstElementById(param + '_SUG');
     suge.component.callSuggestion(true);
    }
    
    function myAjax(param) {
     if (reRender != null) {
     return;
     }
     // rerender a bit later (so execute the real re-render after the selection for example)
     reRender = setTimeout( param + '(); reRender=null;', 250);
    }
    
    
    


  • 11. Re: How To: Check and modify the values from h:inputTexts wh
    sureshtechspot Newbie

    Can any one tell me how to implement this work around for the below two prblems in RF 3.1.5

     

    1. Selected suggestion is disappering from the screen when onchange envet is triggered.

    2. Selected suggestion is replaced by the old value ( which is typed charactes ).

     

    There is no <a4j:queue> in 3.1.5 so I need help in this version. Your help will be appreciacted.

  • 12. Re: How To: Check and modify the values from h:inputTexts wh
    Damir Galimov Newbie

    It seems to me, i have a more easy solution with the same idea:

    <h:panelGroup xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
              xmlns:rich="http://richfaces.org/rich" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:a4j="http://richfaces.org/a4j"
              id="suggestControl">
              <h:inputText value="#{controller.value}" id="suggestInputCode">
                        <a4j:support event="onblur" requestDelay="250" reRender="suggestControl"></a4j:support>
              </h:inputText>
              <h:outputText value="#{controller.displayValue}" id="suggestDisplayValue" />
              <rich:suggestionbox id="#suggestBox" for="suggestInputCode" suggestionAction="#{controller.autoComplete}" var="result"
                        fetchValue="#{result.id}" nothingLabel="Not found" ignoreDupResponses="true">
                        <ui:insert name="suggestTable">
                                  <h:column>#{result.id}</h:column>
                        </ui:insert>
                        <a4j:support event="onselect" reRender="suggestControl" />
              </rich:suggestionbox>
    </h:panelGroup>
    
  • 13. Re: How To: Check and modify the values from h:inputTexts wh
    Immo Benjes Newbie

    Hi Damir,

     

    I've tried your simpler solution but found that while it works with Chrome or Safari it doesn't seem to work on Firefox for me.

     

    Selection with the cursor keys works but not with the mouse.