Primefaces p:confirmDialog inside tabView

Thien Dinh picture Thien Dinh · Feb 2, 2014 · Viewed 10.5k times · Source

I now have problem with using confirmDialog inside tabView. Here is my confirmDialog

<p:confirmDialog global="true" showEffect="fade" hideEffect="explode">
    <h:form>
        <p:commandButton value="Yes" type="button"
            styleClass="ui-confirmdialog-yes" icon="ui-icon-check" />
        <p:commandButton value="No" type="button"
            styleClass="ui-confirmdialog-no" icon="ui-icon-close" />
    </h:form>
</p:confirmDialog>

In the 1st tabView, I have a button with confirmation

<p:commandButton value="Import" icon="ui-icon-arrowrefresh-1-w"
    update=":form:growl">
    <p:confirm header="Confirmation" message="Are you sure?" />
</p:commandButton>

In the 2nd tabView, I have exactly that button.

And now my problem is: In the 1st tab, my confirmDialog have full text as I want: header and message, however in the 2nd tab, the header and message all become "null". Only button yes and no of confirmDialog still work. I don't know what is happening, please help me, how to make confirmDialog display full content in all tabView

Answer

Vsevolod Golovanov picture Vsevolod Golovanov · Sep 25, 2014

p:confirm does not implement state saving, thus it loses its attributes' values after the first JSF lifecycle. It also evaluates EL only once at view build time.

How to fix state saving.

You can extends PrimeFaces' ConfirmBehavior, implement saveState, restoreState, and override the original behavior in faces-config.xml by behavior-id org.primefaces.behavior.ConfirmBehavior. Then you'll be able to render and re-render p:confirm on subsequent requests.

How to fix state saving and reevaluate EL bound attribute values.

You should create your own my:confirm, because you need a custom taghandler, and you can't substitute a taghandler of another taglibrary's tag in a non-ugly way.

Create ConfirmBehavior.

package my;

import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.behavior.ClientBehaviorContext;
import javax.faces.context.FacesContext;

import org.primefaces.behavior.base.AbstractBehavior;
import org.primefaces.component.api.Confirmable;
import org.primefaces.json.JSONObject;

public class ConfirmBehavior extends AbstractBehavior {

    public final static String BEHAVIOR_ID = "my.ConfirmBehavior";

    @Override
    public String getScript(ClientBehaviorContext behaviorContext) {
        FacesContext context = behaviorContext.getFacesContext();
        UIComponent component = behaviorContext.getComponent();
        String source = component.getClientId(context);
        String headerText = JSONObject.quote(this.getHeader());
        String messageText = JSONObject.quote(this.getMessage());

        if (component instanceof Confirmable) {
            String script = "PrimeFaces.confirm({source:\"" + source + "\",header:" + headerText + ",message:"
                    + messageText + ",icon:\"" + getIcon() + "\"});return false;";
            ((Confirmable) component).setConfirmationScript(script);

            return null;
        } else {
            throw new FacesException("Component " + source + " is not a Confirmable. ConfirmBehavior can only be "
                    + "attached to components that implement org.primefaces.component.api.Confirmable interface");
        }
    }

    public String getHeader() {
        return eval(PropertyKeys.header, null);
    }

    public void setHeader(String header) {
        setLiteral(PropertyKeys.header, header);
    }

    public String getMessage() {
        return eval(PropertyKeys.message, null);
    }

    public void setMessage(String message) {
        setLiteral(PropertyKeys.message, message);
    }

    public String getIcon() {
        return eval(PropertyKeys.icon, null);
    }

    public void setIcon(String icon) {
        setLiteral(PropertyKeys.icon, icon);
    }

    public enum PropertyKeys {
        header(String.class), message(String.class), icon(String.class);

        final Class<?> expectedType;

        PropertyKeys(Class<?> expectedType) {
            this.expectedType = expectedType;
        }
    }

    @Override
    protected Enum<?>[] getAllProperties() {
        return PropertyKeys.values();
    }

}

Create ConfirmBehaviorHandler.

package my;

import javax.faces.application.Application;
import javax.faces.view.facelets.BehaviorConfig;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagAttribute;

import org.primefaces.behavior.base.AbstractBehaviorHandler;

public class ConfirmBehaviorHandler extends AbstractBehaviorHandler<ConfirmBehavior> {

    private final TagAttribute header;
    private final TagAttribute message;
    private final TagAttribute icon;

    public ConfirmBehaviorHandler(BehaviorConfig config) {
        super(config);
        this.header = this.getAttribute(ConfirmBehavior.PropertyKeys.header.name());
        this.message = this.getAttribute(ConfirmBehavior.PropertyKeys.message.name());
        this.icon = this.getAttribute(ConfirmBehavior.PropertyKeys.icon.name());
    }

    @Override
    protected ConfirmBehavior createBehavior(FaceletContext ctx, String eventName) {
        Application application = ctx.getFacesContext().getApplication();
        ConfirmBehavior behavior = (ConfirmBehavior) application.createBehavior(ConfirmBehavior.BEHAVIOR_ID);

        setBehaviorAttribute(ctx, behavior, this.header, ConfirmBehavior.PropertyKeys.header.expectedType);
        setBehaviorAttribute(ctx, behavior, this.message, ConfirmBehavior.PropertyKeys.message.expectedType);
        setBehaviorAttribute(ctx, behavior, this.icon, ConfirmBehavior.PropertyKeys.icon.expectedType);

        return behavior;
    }

}

Register ConfirmBehavior in faces-config.xml.

<?xml version="1.0" encoding="utf-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">

    <behavior>
        <behavior-id>my.ConfirmBehavior</behavior-id>
        <behavior-class>my.ConfirmBehavior</behavior-class>
    </behavior>

</faces-config>

Register ConfirmBehaviorHandler in your taglibrary my.taglib.xml.

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd"
    version="2.2">

    <namespace>http://mycompany.ru/my</namespace>

    <tag>
        <tag-name>confirm</tag-name>
        <behavior>
            <behavior-id>my.ConfirmBehavior</behavior-id>
            <handler-class>my.ConfirmBehaviorHandler</handler-class>
        </behavior>
    </tag>

</facelet-taglib>

Now you can use my:confirm, just as you would use p:confirm, but with state saving and dynamic EL evaluation.