h:commandButton/h:commandLink does not work on first click, works only on second click

前端 未结 2 1683
北海茫月
北海茫月 2020-11-22 05:23

We have an ajax navigation menu which updates a dynamic include. The include files have each their own forms.


    

        
2条回答
  •  囚心锁ツ
    2020-11-22 05:59

    Thanks to BalusC since his answer is really great (as usual :) ). But I have to add that this approach does not work for ajax requests coming from RichFaces 4. They have several issues with ajax and one of them is that the JSF-ajax-handlers are not being invoked. Thus, when doing a rerender on some container holding a form using RichFaces-components, the fixViewState-function is not called and the ViewState is missing then.

    In the RichFaces Component Reference, they state how to register callbacks for "their" ajax-requests (in fact they're utilizing jQuery to hook on all ajax-requests). But using this, I was not able to get the ajax-response which is used by BalusC's script above to get the ViewState.

    So based on BalusC's fix, i worked out a very similar one. My script saves all ViewState-values of all forms on the current page in a map before the ajax-request is being processed by the browser. After the update of the DOM, I try to restore all ViewStates which have been saved before (for all forms which are missing the ViewState now).

    Move on:

    jQuery(document).ready(function() {
        jQuery(document).on("ajaxbeforedomupdate", function(args) {
            // the callback will be triggered for each received JSF AJAX for the current page
            // store the current view-states of all forms in a map
            storeViewStates(args.currentTarget.forms);
        });
        jQuery(document).on("ajaxcomplete", function(args) {
            // the callback will be triggered for each completed JSF AJAX for the current page
            // restore all view-states of all forms which do not have one
            restoreViewStates(args.currentTarget.forms);
        });
    });
    
    var storedFormViewStates = {};
    
    function storeViewStates(forms) {
        storedFormViewStates = {};
        for (var formIndex = 0; formIndex < forms.length; formIndex++) {
            var form = forms[formIndex];
            var formId = form.getAttribute("id");
            for (var formChildIndex = 0; formChildIndex < form.children.length; formChildIndex++) {
                var formChild = form.children[formChildIndex];
                if ((formChild.hasAttribute("name")) && (formChild.getAttribute("name").match(/^([\w]+:)?javax\.faces\.ViewState(:[0-9]+)?$/))) {
                    storedFormViewStates[formId] = formChild.value;
                    break;
                }
            }
        }
    }
    
    function restoreViewStates(forms) {
        for (var formIndexd = 0; formIndexd < forms.length; formIndexd++) {
            var form = forms[formIndexd];
            var formId = form.getAttribute("id");
            var viewStateFound = false;
            for (var formChildIndex = 0; formChildIndex < form.children.length; formChildIndex++) {
                var formChild = form.children[formChildIndex];
                if ((formChild.hasAttribute("name")) && (formChild.getAttribute("name").match(/^([\w]+:)?javax\.faces\.ViewState(:[0-9]+)?$/))) {
                    viewStateFound = true;
                    break;
                }
            }
            if ((!viewStateFound) && (storedFormViewStates.hasOwnProperty(formId))) {
                createViewState(form, storedFormViewStates[formId]);
            }
        }
    }
    
    function createViewState(form, viewState) {
        var hidden;
    
        try {
            hidden = document.createElement(""); // IE6-8.
        } catch(e) {
            hidden = document.createElement("input");
            hidden.setAttribute("name", "javax.faces.ViewState");
        }
    
        hidden.setAttribute("type", "hidden");
        hidden.setAttribute("value", viewState);
        hidden.setAttribute("autocomplete", "off");
        form.appendChild(hidden);
    }
    

    Since I am not an JavaScript-expert, I guess that this may be improved further. But it definitely works on FF 17, Chromium 24, Chrome 12 and IE 11.

    Two additional questions to this approach:

    • Is it feasible to use the same ViewState-value again? I.e. is JSF assigning the same ViewState-value to each form for every request/response? My approach is based on this assumption (and I have not found any related information).

    • Does someone expect any problems with this JavaScript-code or already ran into some using any browser?

提交回复
热议问题