julianomqs
julianomqs

Reputation: 118

How to defer PrimeFaces.settings and Client Side Validation scripts with o:deferredScript

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

Upvotes: 2

Views: 1843

Answers (1)

BalusC
BalusC

Reputation: 1109262

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>

Upvotes: 1

Related Questions