Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADF af:showPrintablePageBehavior sends all subsequent navigation commands to a new window

I have a print icon that renders the printable version of the form.

<af:link id="printButton" icon="/images/printer.png">
   <af:showPrintablePageBehavior/>
</af:link>

This part works properly, but after you close the tab created by the <af:showPrintablePageBehavior/> any attempts to redirect to a new page creates a new browser tab.

The button doing the redirect is defined thusly,

<af:button text="Search" action="#{backing.searchAction}" 
           partialSubmit="false" immediate="true" id="ab1" />

public String searchAction() {
    return "search" 
}

"search" is a navigation-rule, which is defined in the faces-config.xml and works properly if you do not invoke <af:showPrintablePageBehavior/> before clicking the button.

I have tried using ExternalContext.redirect(page). I have also tried defining the targetFrame attribute to _self and _parent for the <af:button>. The miss-behavior is consistent with each of these approaches.

like image 654
Patrick J Abare II Avatar asked May 18 '15 19:05

Patrick J Abare II


1 Answers

The root of all evil in your example is the immediate="true" attribute. If you just remove it, you'll get your problem solved.

However, in order to explain why, let's first start with the <af:showPrintablePageBehaviour> component and the trinidad-config.xml file (which is nested within the WEB-INF folder).

The trinidad-config.xml file lists an element, called <output-mode>. By default, it's not there, but you can add it manually. The element supports three values:

  • "default" (or null): the default output mode
  • "printable": an output mode suitable for printable pages
  • "email": an output mode suitable for e-mailing a page's content

In order to have a page content displayed as a printable, the output-mode is changed (usually by the underlying framework) to "printable". So, every time you click on your <af:link> component, the <output-mode> value is set to "printable".

The good thing about trinidad-config.xml is that we are allowed to use EL expressions within, which means that we can dynamically change the value of the <output-mode> element. For example, the value can be read from a @ManagedBean:

 <output-mode>#{printableBehaviorBean.outputMode}</output-mode>

The been itself can be a completely simple (@RequestScoped) bean. Note that I'm using annotations instead of XML-based configuration, because you mentioned you're using ADF 12c, which is built on the top of JSF-2.

@RequestScoped
@ManagedBean(name = "printableBehaviorBean")
public class PrintableBehaviorBean {
    private String outputMode;

    public void setOutputMode(String outputMode) {
        this.outputMode = outputMode;
    }

    public String getOutputMode() {
        return outputMode;
    }
}

Then, we can inject this bean within another bean, which will help us navigate to the "search" activity when the Search button is pressed. The bean will have a nested @ManagedProperty member, which will hold an instance of the preceding printableBehaviorBean bean. Also, the navigationBean will introduce a search() method, which we'll be referred from the Search button's action attribute.

The trick here's that before actually returning the navigation outcome, we'll change the value of the outputMode property in the printableBehaviorBean. Remember that this new value will overwrite the existing <output-mode> value and if the <output-mode> value was previously set to "printable", it will be reverted back to "default" (and this will happen every time we click the on Search button).

@RequestScoped
@ManagedBean(name = "navigationBean")
public class NavigationBean {

    @ManagedProperty(name = "printableBehaviorBean", value="#{printableBehaviorBean}")
    private PrintableBehaviorBean printableBehaviorBean;

    public void setPrintableBehaviorBean(PrintableBehaviorBean printableBehaviorBean) {
        this.printableBehaviorBean = printableBehaviorBean;
    }

    public PrintableBehaviorBean getPrintableBehaviorBean() {
        return printableBehaviorBean;
    }

    public String search() {
        printableBehaviorBean.setOutputMode("default");
        return "search";
    }

}

And finally, the Search button definition would be changed slightly to:

<af:button text="Search" action="#{navigationBean.search}" immediate="true" id="ab1" />

Now, another question of interest would be "Why removing immediate="true" solves the problem?.

A UICommand component decorated with immediate="true" will make the framework skip the "Process validations", "Update model values" and "Invoke application" JSF life-cycle phases (i.e. the 3rd, 4th and the 5th phase). My assumption on your problem would be that before the "Invoke application" phase, the ADF implementation checks if the command components is decorated with a <af:showPrintablePageBehaviour> component and if it is, then it programmatically changes the value of the <output-mode> element. And since you have immediate="true" on the "Search" button, this value would be left as "printable". That's why when you go back to the form, it triggers a new tab.

So, in conclusion, either remove the immediate="true" attribute, or follow the workaround with the beans I suggested.

like image 75
Konstantin Yovkov Avatar answered Sep 25 '22 08:09

Konstantin Yovkov