Monday, February 4, 2013

How to prevent tab changing in PrimeFaces TabView when validation failed

Trying various approaches to the question above (with p:ajax event="tabChange" and so on), I came up with a clean and well working solution. The code below turns on the PrimeFaces TabView into a "wizard mode". That means, the user stays on the same tab when he / she is trying to switch tabs and has validation errors within the current tab. The main idea is to use p:remoteCommand for a deferred decision about tab switching. If no validation errors were present, the switching occurs by the TabView widget's method select.

XHTML
<p:tabView ... widgetVar="tv_widget" styleClass="tv_selector">
    ...
</p:tabView>
           
<p:remoteCommand name="tv_remoteCommand" process="@(.tv_selector)" update="@(.tv_selector)" global="false"
        oncomplete="handleTabChangeTV(xhr, status, args, tv_widget)"/>
           
<h:panelGroup id="tv_script" styleClass="tv_selector">
    <script type="text/javascript">
          $(function(){
              bindOnClickTV(tv_remoteCommand, tv_widget);
          });
    </script>
</h:panelGroup>
JavaScript
function bindOnClickTV(rc, widget) {
    // unbind PrimeFaces click handler and bind our own
    widget.jq.find('.ui-tabs-nav > li').unbind('click.tabview').
        off('click.custom-tabview').on('click.custom-tabview', function (e) {
            var element = $(this);
       
            if ($(e.target).is(':not(.ui-icon-close)')) {
                var index = element.index();
       
                if (!element.hasClass('ui-state-disabled') && index != widget.cfg.selected) {
                    // buffer clicked tab
                    widget.clickedTab = element.index();
       
                    // call remote command
                    rc();
                }
            }
       
            e.preventDefault();
        });
}

function handleTabChangeTV(xhr, status, args, widget) {
    if (!args.validationFailed) {
        widget.select(widget.clickedTab);
    }
}

13 comments:

  1. It works great. Thanks!

    ReplyDelete
  2. Tab View Provides more Events like addChild,removeChild,selectionChange,render.It's nice and helpful event for custom Software Developer.Thanks for sharing worth information.

    ReplyDelete
  3. If I understand correctly this will always process and update whole tabView? How about processing only the contents of the tab that was active before the click?

    ReplyDelete
  4. Hey Oleg,

    nice solution. But as The Goldman wrote, is it possible to process the current tab instead of whole tabView?

    ReplyDelete
  5. Yes, it is possible. I have achieved this with PFS (PrimeFaces Selectors). I use something like in process / update of remoteCommand

    @(.filterPanelTabView .ui-tabs-panel:visible > div)

    where filterPanelTabView is the style class of my tabView. You should also ensure that the most top element in each tab is div (see .ui-tabs-panel:visible > div).

    ReplyDelete
  6. Hi Oleg,

    i know that's not a thread but i still have some issues with your solution.
    He process the values of my visible tab correctly but don't updating the incorrect values.

    My DOM looks like

    div id="form:services" class="filterPanelTabView">
    ul class="ui-tabs-nav ui-helper-reset..." role="tablist">
    div class="ui-tabs-panels">
    div id="form:services:j_idt109" class="ui-tabs-panel..." role="tabpanel">
    table cellpadding="5">
    ...

    Any idea?

    Regards

    glister

    ReplyDelete
  7. Update:

    okay, i had to encapsulate my table in an h:panelGroup, now it works. :D

    Before your solution, i had a tabChange event listener which set an boolean variable if clicked tab title == XYZ.
    But if i use an tabChange listener AND p:remoteComand, there 2 ajax calls every tab change. The first from p:remoteComand and the second from ajax tabChange listener.

    Do you know how to send the active tab to backing bean? I tried it with activeIndex property from tabView, but no success.

    Thanks,

    Glister

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. Hi, I am facing the same issue and I just want to make sure a couple of things before I try this out. Does this work in case of dynamic tabs ? So, I have something like :












    Now, my question is, in this case, I need to validate the activeTab's components and onTabChange it should validate activeTab's form only(not submit), after I finish entering all the tabs, I have a single submit button outside the tabView where I should submit all the forms. Is this even possible ? If yes, can I use this solution somehow ? Help Appreciated!

    ReplyDelete
  10. Sorry I think the content was missing above ,
    Hi, I am facing the same issue and I just want to make sure a couple of things before I try this out. Does this work in case of dynamic tabs ? So, I have something like :
    http://stackoverflow.com/questions/21896371/ptabview-dynamic-tabs-with-dynamic-forms
    Now, my question is, in this case, I need to validate the activeTab's components and onTabChange it should validate activeTab's form only(not submit), after I finish entering all the tabs, I have a single submit button outside the tabView where I should submit all the forms. Is this even possible ? If yes, can I use this solution somehow ? Help Appreciated!

    ReplyDelete
  11. Hello, sandeep, did you find a solution for your problem with the tabView, i'm facing the same problem, i would like to validate a single tab before move onto the next.

    ReplyDelete

Note: Only a member of this blog may post a comment.