سؤال

I've implemented the following custom component in my application:

@FacesComponent ("org.app.component.hintBubble")
public class TutorialHintBubbleComponent extends UIComponentBase implements Serializable {

private static final long serialVersionUID = -8124906197708898894L;

public static final String COMPONENT_TYPE = "org.app.component.hintBubble";

@Override
public String getFamily() {
    return COMPONENT_TYPE;
}

@Override
public boolean isTransient() {
    return false;
}

@Override
public void encodeBegin(FacesContext context) throws IOException {
    setId("hintBubble");

    TutorialHintBubble value = (TutorialHintBubble) this.getValueExpression("value").getValue(context.getELContext());
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement("div", this);
        writer.writeAttribute("style", value.getCss().getBodyCss(), null);
        writer.writeAttribute("class", "hint-bubble", null);

        if ( value.getPointer().getLocation() != HintBubblePoinerLocation.NONE ) {
            writer.startElement("div", this);
                writer.writeAttribute("style", value.getCss().getPointerCss(), null);
                writer.writeAttribute("class", "hint-bubble-pointer", null);
            writer.endElement("div");

            if ( value.getBorder().getThicknessInPixels() > 0 ) {
                writer.startElement("div", this);
                    writer.writeAttribute("style", value.getCss().getPointerBorderCss(), null);
                    writer.writeAttribute("class", "hint-bubble-pointer-border", null);
                writer.endElement("div");
            }
        }



        writer.startElement("div", this);
            writer.writeAttribute("class", "hint-bubble-inner-html-container", null);
            writer.write(value.getInnerHtml());
        writer.endElement("div");

        if ( value.isShowConfirmButton() ) {
            writer.startElement("div", this);
            writer.writeAttribute("class", "hint-bubble-btn-container", null);

            UICommandLink commandLink = new UICommandLink();
            getChildren().add(commandLink);
            commandLink.setParent(this);

            commandLink.setValue(value.getButtonCaption());
            commandLink.setStyleClass("hint-bubble-btn");
            commandLink.setId("okButton");

            ValueExpression actionListenerExpression = getValueExpression("actionListener");

            if ( actionListenerExpression != null ) {
                commandLink.addActionListener(
                    (ActionListener) actionListenerExpression.getValue(context.getELContext())
                );
            }
        }
    }


@Override
public void encodeEnd(FacesContext context) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    TutorialHintBubble value = (TutorialHintBubble) this.getValueExpression("value").getValue(context.getELContext());
    if ( value.isShowConfirmButton() ) {
        writer.endElement("div");
    }

    writer.endElement("div");
}

}

As you can see there is UICommandLink added as a child to this component. Attached to this command link is ActionListener. The ActionListener is evaluated from expression passed as a parameter to HintBubble component. Debugging shows that action listener is correctly evaluated and added to UICommandLink.

Code in xhtml:

<h:form id="tutorialForm">
<a4j:outputPanel id="tutorialContainer" layout="block" >
    <a4j:repeat value="#{tutorialBean.hintBubbles}" var="hintBubble">
        <gg:hintBubble value="#{hintBubble}" actionListener="#{tutorialManager}" />
    </a4j:repeat>
</a4j:outputPanel>
</h:form>

Everything is correctly rendered on webpage but when I click the button action is not performed. (Although ajax request is sent to the server)

My question is:

In which method should I add the UICommandLink to component's children in order to get things working? (Note that UICommandLink is from richfaces i.e. it is org.richfaces.component.UICommandLink)

هل كانت مفيدة؟

المحلول

I finally managed to get this working. The JSF Component which I presented in question should have three classes: Component, Renderer, and ComponentHandler.

The child UICommandLink should be added to HintBubbleComponent in its ComponentHandler in method on Component created:

public class HintBubbleHandler extends ComponentHandler {

public HintBubbleHandler(ComponentConfig config) {
    super(config);
}

@Override
public void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
    HintBubbleComponent hintBubbleComponent = (HintBubbleComponent) c;

    UICommandLink commandLink = new UICommandLink();
    hintBubbleComponent.getChildren().add(commandLink);
    commandLink.setParent(hintBubbleComponent);
    commandLink.setId("okButton");      
}

}

Thanks to that, UICommandLink will be present in the component tree when the tree is visited by callback looking for component which executed action.

The actionListener should be set to UICommandLink in decode() method of the renderer:

@Override
public void decode(FacesContext context, UIComponent component) {
    super.decode(context, component);

    UICommandLink commandLink = (UICommandLink) component.getChildren().get(0);

    MethodExpression actionListener =  ((HintBubbleComponent) component).getActionListener();

    if ( actionListener != null ) {
        commandLink.setActionExpression(actionListener);
    }

}   

Now everything is working at the action is invoked !

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top