All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.omnifaces.component.input.OnloadParam Maven / Gradle / Ivy

There is a newer version: 4.5.1
Show newest version
/*
 * Copyright OmniFaces
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.omnifaces.component.input;

import static jakarta.faces.event.PhaseId.RENDER_RESPONSE;
import static org.omnifaces.config.OmniFaces.OMNIFACES_EVENT_PARAM_NAME;
import static org.omnifaces.config.OmniFaces.OMNIFACES_LIBRARY_NAME;
import static org.omnifaces.config.OmniFaces.OMNIFACES_SCRIPT_NAME;
import static org.omnifaces.util.Ajax.isExecuted;
import static org.omnifaces.util.Ajax.update;
import static org.omnifaces.util.Components.addFacesScriptResource;
import static org.omnifaces.util.Components.addFormIfNecessary;
import static org.omnifaces.util.Components.addScript;
import static org.omnifaces.util.Components.addScriptResource;
import static org.omnifaces.util.Events.subscribeToRequestBeforePhase;
import static org.omnifaces.util.FacesLocal.getRequestMap;
import static org.omnifaces.util.FacesLocal.getRequestParameter;
import static org.omnifaces.util.FacesLocal.isAjaxRequestWithPartialRendering;

import jakarta.faces.component.UIViewParameter;
import jakarta.faces.context.FacesContext;

import org.omnifaces.config.OmniFaces;
import org.omnifaces.util.State;

/**
 * Base class of {@link HashParam} and {@link ScriptParam}.
 *
 * @since 3.6
 */
public abstract class OnloadParam extends UIViewParameter {

    // Private constants ----------------------------------------------------------------------------------------------

    private enum PropertyKeys {
        RENDER;
        @Override public String toString() { return name().toLowerCase(); }
    }

    // Variables ------------------------------------------------------------------------------------------------------

    /** The component state. */
    protected final State state = new State(getStateHelper());

    // Init -----------------------------------------------------------------------------------------------------------

    /**
     * The constructor instructs Faces to register all scripts during the render response phase if necessary.
     */
    protected OnloadParam() {
        subscribeToRequestBeforePhase(RENDER_RESPONSE, this::registerScriptsIfNecessary);
    }

    private void registerScriptsIfNecessary() {
        FacesContext context = getFacesContext();

        if (!isOnloadParamRequest(context)) {
            addFormIfNecessary(); // Required by jsf.ajax.request.

            // This is supposed to be declared via @ResourceDependency. But this bugs in Mojarra with NPE on
            // ViewMetadata#createMetadataView because UIViewRoot is null at the moment the f:metadata is processed.
            // Also, JSF 3 and Faces 4 use a different script resource name which cannot be resolved statically.
            addFacesScriptResource(); // Required for faces.ajax.request.
            addScriptResource(OMNIFACES_LIBRARY_NAME, OMNIFACES_SCRIPT_NAME);

            if (!isAjaxRequestWithPartialRendering(context)) {
                if (getRequestMap(context).put(getClass().getName(), Boolean.TRUE) == null) { // Just init only once for first encountered OnloadParam.
                    String initScript = getInitScript(context);

                    if (initScript != null) {
                        addScript(initScript);
                    }
                }
            }
            else {
                String updateScript = getUpdateScript(context);

                if (updateScript != null) {
                    addScript(updateScript);
                }
            }
        }
    }

    /**
     * Returns script which should be executed upon initialization of a new view.
     * @param context The involved faces context.
     * @return Script which should be executed upon initialization of a new view.
     */
    protected String getInitScript(FacesContext context) {
        return null;
    }

    /**
     * Returns script which should be exeucted upon ajax update of the current view.
     * @param context The involved faces context.
     * @return Script which should be exeucted upon ajax update of the current view.
     */
    protected String getUpdateScript(FacesContext context) {
        return null;
    }

    // Actions --------------------------------------------------------------------------------------------------------

    /**
     * Returns the value of the {@link OmniFaces#OMNIFACES_EVENT_PARAM_NAME} associated with the current component.
     * @param context The involved faces context.
     * @return The value of the {@link OmniFaces#OMNIFACES_EVENT_PARAM_NAME} associated with the current component.
     */
    protected abstract String getEventValue(FacesContext context);

    /**
     * Returns true if the current request was invoked by the current {@link OnloadParam} component.
     * @param context The involved faces context.
     * @return true if the current request was invoked by the current {@link OnloadParam} component.
     */
    protected boolean isOnloadParamRequest(FacesContext context)
    {
        return isOnloadParamRequest(context, getEventValue(context));
    }

    /**
     * If this is invoked during an OnloadParam postback, then decode as if immediate=true to save lifecycle overhead.
     */
    @Override
    public void processDecodes(FacesContext context) {
        if (isOnloadParamRequest(context) && isExecuted(getClientId(context))) {
            decodeAll(context);
            context.renderResponse();
        }
    }

    /**
     * Decode all relevant {@link OnloadParam} components at once.
     * @param context The involved faces context.
     */
    protected abstract void decodeAll(FacesContext context);

    /**
     * This basically acts as if immediate=true is set to save lifecycle overhead.
     * @param context The involved faces context.
     * @param submittedValue The submitted value.
     */
    protected void decodeImmediately(FacesContext context, String submittedValue) {
        setSubmittedValue(submittedValue);
        validate(context);

        if (isValid()) {
            updateModel(context);
        }
        else {
            setValid(true); // Don't leave OnloadParam in an invalid state for next postback as it would block processing of regular forms.
        }

        String render = getRender();

        if (render != null) {
            update(render.split("\\s+"));
        }
    }

    /**
     * This override which does effectively nothing prevents Faces from performing validation during non-onloadparam postbacks.
     */
    @Override
    public void processValidators(FacesContext context) {
        // NOOP.
    }

    /**
     * This override which does effectively nothing prevents Faces from performing update during non-onloadparam postbacks.
     */
    @Override
    public void processUpdates(FacesContext context) {
        // NOOP.
    }

    // Attribute getters/setters --------------------------------------------------------------------------------------

    /**
     * Returns a space separated string of client IDs to update on ajax response.
     * @return A space separated string of client IDs to update on ajax response.
     */
    public String getRender() {
        return state.get(PropertyKeys.RENDER, "@none");
    }

    /**
     * Sets a space separated string of client IDs to update on ajax response.
     * @param render A space separated string of client IDs to update on ajax response.
     */
    public void setRender(String render) {
        state.put(PropertyKeys.RENDER, render);
    }

    // Helpers --------------------------------------------------------------------------------------------------------

    /**
     * Returns true if the current request is triggered by an onload param request of the given onload param event.
     * @param context The involved faces context.
     * @param onloadEvent The onload param event.
     * @return true if the current request is triggered by an onload param request of the given onload param event.
     */
    protected static boolean isOnloadParamRequest(FacesContext context, String onloadEvent)
    {
        return context.isPostback() && onloadEvent.equals(getRequestParameter(context, OMNIFACES_EVENT_PARAM_NAME));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy