2

I'm trying to use OmniFaces to defer PrimeFaces scripts, as seen in this answer.

But PrimeFaces render an inline script in head, like this (script beautified and commented by me):

<script type="text/javascript">
    if(window.PrimeFaces) {
        // this line is always rendered in Development mode.
        PrimeFaces.settings.projectStage='Development';
        // these lines are added if Client Side Validation is enabled.
        PrimeFaces.settings.locale='pt_BR';
        PrimeFaces.settings.validateEmptyFields=true;
        PrimeFaces.settings.considerEmptyStringNull=true;
    }
</script>

When I run the application, I get some JS errors (file and error):

validation.js.xhtml?ln=primefaces&v=5.3:1
Uncaught TypeError: Cannot read property 'en_US' of undefined

beanvalidation.js.xhtml?ln=primefaces&v=5.3:1
Uncaught TypeError: Cannot read property 'en_US' of undefined

produto.xhtml:2
Uncaught TypeError: Cannot set property 'locale' of undefined

If I put some variables in primefaces.deferred.js, like this:

if (!primeFacesLoaded) {
    window.PrimeFaces = {
        // variables added - begin
        settings: {
            projectStage: 'Development',
            locale: 'pt_BR',
            validateEmptyFields: true,
            considerEmptyStringNull: true
        },
        // variables added - end
        ab: function () {
            defer("ab", arguments);
        },
        cw: function () {
            defer("cw", arguments);
        },
        focus: function () {
            defer("focus", arguments);
        }
    };
}

The two first errors still occur, but the third error go away.

Apparently, the PrimeFaces JS object lacks the following properties:

locales: {
    // other values...
    en_US: {
        // other values...
    }
},
util: {
    // other values...
},

So, the question is: How to defer these PrimeFaces script properties correctly?

P.S: Versions: PrimeFaces 5.3, OmniFaces 2.3, Payara Server (Glassfish) 4.1.1.161

Community
  • 1
  • 1
julianomqs
  • 108
  • 5

1 Answers1

1

The PrimeFaces.settings was introduced after the answer was posted. The answer has in the meanwhile been updated to take that into account. The updated script is:

DeferredPrimeFaces = function() {
    var deferredPrimeFaces = {};
    var calls = [];
    var settings = {};
    var primeFacesLoaded = !!window.PrimeFaces;

    function defer(name, args) {
        calls.push({ name: name, args: args });
    }

    deferredPrimeFaces.begin = function() {
        if (!primeFacesLoaded) {
            settings = window.PrimeFaces.settings;
            delete window.PrimeFaces;
        }
    };

    deferredPrimeFaces.apply = function() {
        if (window.PrimeFaces) {
            for (var i = 0; i < calls.length; i++) {
                window.PrimeFaces[calls[i].name].apply(window.PrimeFaces, calls[i].args);
            }

            window.PrimeFaces.settings = settings;
        }

        delete window.DeferredPrimeFaces;
    };

    if (!primeFacesLoaded) {
        window.PrimeFaces = {
            ab: function() { defer("ab", arguments); },
            cw: function() { defer("cw", arguments); },
            focus: function() { defer("focus", arguments); },
            settings: {}
        };
    }

    return deferredPrimeFaces;
}();

Basically, just prepare the property as an empty object, copy it during begin and then set it during apply.

As to deferring PrimeFaces validation.js and beanvalidation.js files, this requires a custom renderer for <h:head>, as PrimeFaces one actually hardcodes them instead of declaring them as @ResourceDependency. You can find a concrete example in wiki of CombinedResourceHandler.

package com.example;

import java.io.IOException;

import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.render.Renderer;

@ResourceDependencies({
    @ResourceDependency(library="primefaces-aristo", name="theme.css"),
    @ResourceDependency(library="primefaces", name="primefaces.js"), // Only necessary when at least one validation JS files needs to be included.
    @ResourceDependency(library="primefaces", name="validation/validation.js"), // Only necessary when you need <p:clientValidator>.
    @ResourceDependency(library="primefaces", name="validation/beanvalidation.js") // Only necessary when you use JSR303 bean validation.
})
public class HeadRenderer extends Renderer {

    @Override
    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
        context.getResponseWriter().startElement("head", component);
    }

    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        // NOOP.
    }

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
        for (UIComponent resource : context.getViewRoot().getComponentResources(context, "head")) {
            resource.encodeAll(context);
        }

        context.getResponseWriter().endElement("head");
    }

}

To get it to run, register it as follows in faces-config.xml:

<render-kit>
    <renderer>
        <component-family>javax.faces.Output</component-family>
        <renderer-type>javax.faces.Head</renderer-type>
        <renderer-class>com.example.HeadRenderer</renderer-class>
    </renderer>
</render-kit>
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Using this script, I still have problems with the locale and util variables. Suggestion: To avoid copying many variables, what if we clone the entire window.PrimeFaces object to another variable, defer ab, cw, focus functions and restore the object later? – julianomqs May 02 '16 at 17:10
  • I see, PrimeFaces has those validation filed hardcoded. See updated answer. – BalusC May 03 '16 at 12:19
  • 2 questions: 1) I have a `scripts.js` that sets `PrimeFaces.locales['pt_BR']` values (to calendar). Should I put this script between `DeferredPrimeFaces.begin()`and `DeferredPrimeFaces.apply()`? 2) I have to specify scripts to defer in every page, or I can put them in a template? – julianomqs May 04 '16 at 22:38
  • Thanks a lot for all the answers. – julianomqs May 05 '16 at 13:59
  • May I ask about the differences of PrimeFaces and window.PrimeFaces as I can access settings by 'window.PrimeFaces.settings' as PrimeFaces.settings will not work. And at begin method, the settings is undefined, so later on I got problem with getLocaleSettings() method when doing validation – Toan Lu Aug 17 '16 at 08:53