1 2 Previous Next 24 Replies Latest reply on Nov 20, 2012 3:59 AM by Lukáš Fryč

    FocusManager proposition

    Bernard Labno Master

      Hello,

      I haven't found any component in Richfaces that would put focus on some element on page load.

      Here is my proposition. Let's make rich:focus tag with "priority" attribute. Default priority would be position of input in form. This parameter will be usefull if we want to focus on first invalid element in form. Tag with lowest priority value will be focused.

       

      if (!window.Richfaces) window.Richfaces = {};
      
      Richfaces.FocusManager = (function(){
      
          var m_focus;
          var m_priority = 999999;
          var eventAttached = false;
      
          var attachEvent = function() {
              if(!eventAttached) {
                  document.observe('dom:loaded',function(){
                      var element = $(m_focus);
                      element.focus();
                      element.select(element);
      
                  });
              }
              eventAttached = true;
          };
      
          return {
              getFocus : function() {
                  return m_focus;
              },
              setFocus : function(id,priority) {
                  if(priority == null) {
                      priority = 99999;
                  }
                  if(m_focus == null || priority < m_priority) {
                      m_focus = id;
                      m_priority = priority == null ? 0 : priority;
                      attachEvent();
                  }
              },
              clearFocus : function() {
                  m_focus = null;
                  m_priority = 999999;
              }
          };
      })();
      

      XHTML source should look like this :

      <h:form>
           <h:inputText value="#{somevalue}">
                <rich:focus/>
           </h:inputText>
      </h:form>
      

      Which would give priority = 1 and should produce following javascript :

      Richfaces.FocusManager.setFocus("f:b",9999);
      

      Other example:

      <h:form>
           <h:inputText value="#{somevalue}">
                <rich:focus priority="3"/>
           </h:inputText>
      </h:form>
      

      Which would give priority = 3 and should produce following javascript :

      Richfaces.FocusManager.setFocus("f:b",3);
      

      Another example :

      <h:form>
           <h:inputText value="#{somevalue}" id="a">
                <rich:focus priority="9999" rendered="#{not invalid}/>
                <rich:focus rendered="#{invalid}"/>
           </h:inputText>
           <h:inputText value="#{somevalue2}" id="b">
                <rich:focus rendered="#{invalid}"/>
           </h:inputText>
      </h:form>

      That would cause following :

      1. if there is no validation error (i.e. initail request), focus is put on "a"
      2. if there is validation failure on "a" or on both, focus is placed on "a" due to lower priority (second focus child tag will be rendered with default priority calculated on position of "a" in enclosing form)
      3. if there is validation failure on "b" only, focus is placed on "b" because it's priority will be 2 (default) and "a" will have priority 9999 since first focus child tag will be rendered

       

      I wonder if logic presented in javascript shouldn't be kept on server to avoid verbose javascript (suggested solution prints separate javascript for each rich:focus tag). I just don't know where if so.

       

      Edited after several month:

      Demo, download and subversion address are here.

       

       

      Make sure to use default script and style loading strategies.

        • 1. Re: FocusManager proposition
          Ilya Shaikovsky Master

          Moving the thread into RichFaces development space as seems good point to discuss for future.

           

          Currently the way it could be achieved:

           

          • you could define ajaxListener - it's invoked before renderResponce so will be called also if validation failed
          • in the listener you could check if there are facesMessages added and check for which components
          • then setup focus attribute EL expression with component Id.

           

          Inconvinience of such solution - you have to define focus with some EL at every component which need this functionality.

          • 2. Re: FocusManager proposition
            Bernard Labno Master

            Hi Ilya. I provide patch for sandbox that creates rich:focus and focus-sample.

             

            Problem with listener is that you need extra logic that will tell input fields from i.e. rich:calendar. And it is not as easy as putting tag.

            We already have to put tags like effect, support, message etc. on each input, so it is inconvenience we all are used to.

             

            My implementation of default priority traverses all enclosing form to calculate position of input in form. I consider it a poor idea (cpu consumption), but cannot find any better. Any suggestions ?

             

            Also I do not know how to check from facelet if component's value is invalid with standard JSF EL (no jboss-el). Hence no cool sample of <rich:focus rendered="#{invalid}"/>

             

            I've added support for radio buttons.
            If we specify <h:selectOneRadio id="option"> then each radio item recives suffix i.e.: ":0".
            To deal with this I added "suffix" attribute, which is added to clientId.

             

            Message was edited by: Bernard Labno

            • 3. Re: FocusManager proposition
              Ilya Shaikovsky Master
              Agree with your statments on my proposal. And thanks for your efforts - we will review!
              • 4. Re: FocusManager proposition
                Bernard Labno Master
                How is the review ? Any progress ? I'm using the code in production and it performs as intended.
                • 5. Re: FocusManager proposition
                  Ilya Shaikovsky Master
                  We not "lost" the thread for sure and will examine before actual implementation. But unfortunatelly focus functionality is not a part of A2. So it will be reviewed after the ALPHA.
                  • 6. Re: FocusManager proposition
                    Nick Belaevski Master

                    Created JIRA issue to cover this component: https://jira.jboss.org/jira/browse/RF-8606

                    • 7. Re: FocusManager proposition
                      Jay Balunas Master

                      Much like your growl idea - I really like this idea, and the proposal.  Good stuff.

                       

                      I think this would focus on the 4.0 milestone release, to be included in there.

                      • 8. Re: FocusManager proposition
                        gonzalad Apprentice

                        Proposition looks nice, but I've just looked at primefaces p:focus component, and it looks a bit easier to use no need to test :

                        rendered="#{not invalid}.

                         

                        Here's primefaces documentation extract :

                         

                        Getting started with Focus

                        In it's simplest form, focus is enabled by just placing the component on the page as;

                        <p:focus />

                        That's it, now let's add some input components to give something to focus on.

                        Input Focus

                        You don't need to explicitly define the component to receive focus as by default focus will

                        find the first enabled and visible input component on page. Input component can be any

                        element such as input, textarea and select. Following is a simple example;

                        <h:form>
                        <p:panel id=”panel” header=”Register”>
                        <p:focus />
                        <p:messages />
                        <h:panelGrid columns=”3”>
                        <h:outputLabel for=”firstname” value=”Firstname: *” />
                        <h:inputText id=”firstname” value=”#{pprBean.firstname}”
                        required=”true” label=”Firstname” />
                        <p:message for=”firstname” />
                        <h:outputLabel for=”surname” value=”Surname: *” />
                        <h:inputText id=”surname” value=”#{pprBean.surname}”
                        required=”true” label=”Surname”/>
                        <p:message for=”surname” />
                        </h:panelGrid>
                        <p:commandButton value=”Submit” update=”panel”
                        actionListener=”#{pprBean.savePerson}” />
                        </p:panel>
                        </h:form>
                        

                        When this page initially opens, input text with id “firstname” will receive focus as it is the

                        first input component.

                        Validation Aware

                        Another useful feature of focus is that when validations fail, first invalid component will

                        receive a focus. So in previous example if firstname field is valid but surname field has no

                        input, a validation error will be raised for surname, in this case focus will be set on

                        surname field implicitly. Note that for this feature to work on ajax requests, you need to

                        update p:focus component as well.

                        Explicit Focus

                        Additionally, using for attribute focus can be set explicitly on an input component.

                         

                        <p:focus for=”text”/>
                        <h:inputText id=”text” value=”{bean.value}” />
                        
                        • 9. Re: FocusManager proposition
                          Ilya Shaikovsky Master

                          simplicity is great point However we always have to choose between simplicity and functionality and should consider different points to find optimal solution.

                           

                          What about next case : after validation failed - developers wants the user to tab between fields where validation failed only? This case not covered by p:focus I believe but looks realistic enough I think to consider it.

                          • 10. Re: FocusManager proposition
                            Bernard Labno Master

                            I've studied p:focus code and it just iterates over facesContext.getClientIdsWithMessages(). I believe it does not guarantee that first control will be focused. Other thing is that we need priority attribute for situations where CSS changes flow of controls (i.e.: flow:right). So statement in p:focus that

                            by default focus will find the first enabled and visible input component on page

                            is false.

                             

                            As to focus after ajax re-render...i guess focus manager should listen to AjaxEvent in broadcast method and use feature of richfaces (ajaxContext I guess) to set appropriate focus.

                             

                            Ilya, your idea is great! Let's add attribute "reorderTabs". If it is set to true then tab order should be set in such a way that after focusing on first invalid field and pressing tab, focus will be granted to next invalid field.

                            I just wonder, if tab order should be set on UIComponents or on client side via JavaScript. This will probably require introducing 2 components: one for establishing focus priority and one for forcing tab reordering.

                            On the other hand...maybe focus manager should not be attached to any tag and no priority should be set, but only that tabindex attribute should be read for deciding where to put the focus on? (Anyways to introduce Ilya's idea reordering would be required, and that would conflict with users settings. Do we just inform him in documentation that his settings (tabindexes) will be overridden by this component?

                             

                            For simplicity we could do this: if parent of focusManager is form than focus is put to first invalid, readable, enabled control (child of the form) in ui component tree. If parent is not the form than focusManager will behave as it does now.

                            • 11. Re: FocusManager proposition
                              gonzalad Apprentice
                              I believe it does not guarantee that first control will be focused.

                               

                              On original request, focus should be positionned on first input component (strange I thought first component would be searched inside h:form containing p:focus and not in all the jsf view).

                               

                              Code extract from org.primefaces.component.focus.FocusRenderer.encodeImplicitFocus(..) - interesting block line 8

                               

                              protected void encodeImplicitFocus(FacesContext facesContext, Focus focus) throws IOException {
                              ResponseWriter writer = facesContext.getResponseWriter();
                               
                              if(isPostBack()){
                              String clientId = findFirstInvalidClientId(facesContext, focus);
                               
                              if(clientId != null)
                              writer.write("jQuery(PrimeFaces.escapeClientId('" + clientId +"')).focus();");
                              } else {
                              writer.write("jQuery(document).ready(function(){");
                              String selector = Focus.INPUT_SELECTOR;
                               
                              if(focus.getContext() != null) {
                              UIComponent context = focus.findComponent(focus.getContext());
                               
                              if(context == null)
                              throw new FacesException("Cannot find component " + focus.getContext() + " in view");
                              else {
                              selector = ComponentUtils.escapeJQueryId(context.getClientId(facesContext)) + " " +  selector;
                              }
                              }
                               
                              writer.write("jQuery('" + selector + "').focus();");
                              writer.write("});");
                              }
                              }
                              
                              
                              

                               

                              With public final static String INPUT_SELECTOR = ":input:not(:submit):visible:enabled:first";

                               

                              Anyway, my point is that it should be great if rich:focus use could be as easy as the p:focus of primefaces for simple - and classical - use cases.

                              My point of view is that the 2 functionnalities indicated in 'Input Focus' and 'Validation Aware' section cover most use cases (let's say 90% ?)

                               

                              How can I achieve it with rich:focus ?

                               

                              Like this ?

                               

                              <h:form>
                                   <h:inputText value="#{somevalue}">
                                        <rich:focus priority="9999" rendered="#{invalid}/>
                                        <rich:focus rendered="#{invalid}"/>
                                   </h:inputText>
                                   <h:inputText value="#{somevalue2}">
                                        <rich:focus rendered="#{invalid}"/>
                                   </h:inputText>
                              </h:form>
                              

                               

                              With p:focus, I have the same functionnality with :

                               

                              <h:form>
                                   <p:focus>
                                   <h:inputText value="#{somevalue}"/>
                                   <h:inputText value="#{somevalue2}"/>
                              </h:form>
                              

                               

                               

                              Have I missed an easier way with rich:focus ?

                               

                              Otherwise, wouldn't it be good to augment rich:focus capabilities to be able to use it outside input components (and in this case it could behave just like p:focus)  ?

                               

                              But perhaps I'm missing a point

                              • 12. Re: FocusManager proposition
                                gonzalad Apprentice
                                For simplicity we could do this: if parent of focusManager is form than focus is put to first invalid, readable, enabled control (child of the form) in ui component tree. If parent is not the form than focusManager will behave as it does now

                                Sorry, didn't see this.

                                 

                                With this functionnality, this component should be great !

                                 

                                Could we rephrase it like that ?:

                                If parent of focusManager is form :

                                - position to first invalid, readable, enabled control.

                                - if no invalid control in form, position it on first input readable, enabled control.

                                • 13. Re: FocusManager proposition
                                  Bernard Labno Master

                                  For clarification:

                                  If parent of focusManager is not UIInput:

                                  - position to first invalid, readable, enabled control in nesting form.

                                  - if no invalid control in form, position it on first input readable, enabled control in nesting form.

                                  Where "first" means left-most in component tree.

                                   

                                  Demo : http://bernard.labno.pl/focus-sample

                                  • 14. Re: FocusManager proposition
                                    gonzalad Apprentice

                                    Great work ! This was fast !

                                    1 2 Previous Next