|
|
 |
Oracle ADF Faces - The ADF Faces Dialog Framework
Overview
Many web applications have a need to jump to a page,
or a series of pages, to get information from the user
and then return to the original page to use that information.
ADF Faces calls these "dialogs". ADF Faces can show
a dialog just by navigating to a new page within your
main browser window, or by showing a popup window.
ADF Faces saves you from the effort of managing
the Javascript needed to launch these windows, and encapsulates
the differences between popup windows and ordinary navigation;
for example, it can switch to using the same window if a client
doesn't support opening new win
dows (a PDA, for example).
This dialog framework can be launched manually
with Java APIs, or more easily with ordinary JSF navigation
rules. You can support it from your own custom UIComponents and
Renderers, or even from a custom RenderKit. It is in part built on
lower-level APIs that support "pushing" and "popping" pages that
you can reuse on their own to show login pages or save state for
multi-page wizards.
Launching Dialogs
The simplest way to interact with the dialog framework is with the
functionality built-in to the
oracle.adf.view.faces.component.UIXCommand
component
class. This class supports all the standard "action" and "actionListener"
attributes of the JSF
UICommand
component, and that's
how you'll use it most of the time. But
UIXCommand
- combined
with some
<
navigation-rule
>
magic - also supports
launching dialogs, and lets you know when that dialog finishes
with a
ReturnEvent
.
To specify a rule that launches a dialog, simply use an
outcome that begins with "dialog:":
<af:commandButton text="Show More Information"
action="dialog:showDetail"/>
...
<navigation-rule>
<from-view-id>/*</from-view-id>
<navigation-case>
<from-outcome>dialog:showDetail</from-outcome>
<to-view-id>/showDetail.jspx</to-view-id>
</navigation-case>
</navigation-rule>
This will show the "/showDetail.jspx" page in the same window.
If you wanted to show it in a popup window, just add useWindow="true"
to the command:
<af:commandButton text="Show More Information"
partialSubmit="true"
useWindow="true"
action="dialog:showDetail"/>
Note:
You cannot use
<
redirect
>
in rules that
may launch dialogs into their own popup windows. It's fine for rules
that do not popup a new window, but instead stay within the same window.
Note that we've also set "partialSubmit" on the commandButton to "true";
we highly recommend using this option on buttons that launch dialogs,
as it avoids an otherwise unnecessary flash of the main page as
the dialog is launched.
You can use a hardcoded outcome from an ADF command component, but,
just as with any "action", you can also programatically decide whether
or not you want to show a dialog (or what dialog to show) from an
ordinary "action" method. For example, an action might check if a
user's been logged out, and if so, show a warning dialog instead of
navigating ordinarily.
<af:commandButton text="Show More Information"
useWindow="true"
action="#{backingBean.goSomewhere}"/>
...
public String goSomewhere()
{
if (isUserLoggedOut())
{
return "dialog:loggedOutWarning";
}
else
{
// Note that "useWindow" is only relevant if
// the outcome begins with "dialog:"; this
// will not show a dialog!
return "somewhere";
}
}
All of these techniques do require that you've defined a navigation
rule with a "dialog:" outcome. Later, we'll see how to
launch a dialog programatically.
In all of these cases, when you're using a web browser
that supports all the features we need for launching dialogs,
a new window will appear containing the dialog. But if you're
using some other web browser or a device like a PDA that doesn't
satisfy our requirements, your dialog code will still work!
Instead of launching a new window, ADF Faces simply show you
the dialog page in the current window after
automatically preserving all the state of your current page.
When the dia
log finishes (more on what that means in a bit), your
command component gets a
ReturnEvent
delivered to a
ReturnListener
, if you've registered one (either with
addReturnListener
or using the "returnListener"
property). The dialog can give you a return value, and if it does,
that will automatically be handed to you as a property of the
ReturnEvent
:
<af:commandButton text="Get Value" action="dialog:getValue"
returnListener="#{backing.handleReturn}"/>
...
public void handleReturn(ReturnEvent event)
{
Object returnedValue = event.getReturnValue();
// ... handle that return value as desired ...
}
The dialog itself can be written like any other JSF page. The
only way in which it differs is that you need to call a special method
to tell ADF Faces that the dialog is complete -
AdfFacesContext.returnFromDialog()
. You can call this
method whether the dialog was shown in a popup window or not; if it was a
popup window, the dialog window will close automatically. You also don't
need to call it in the first page you show - you can navigate from the
first page in the dialog to as many other pages as you want, and just
need to call
returnFromDialog()
eventually. This method
is also what lets you send the "return value" back from your dialog:
<!-- In your dialog page -->
<af:commandButton text="Done" actionListener="#{dialogBacking.done}"/>
...
public void done(ActionEvent e)
{
// Get the return value
Object returnValue = ...;
// And return it
AdfFacesContext.getCurrentInstance().returnFromDialog(returnValue, null);
}
Example
Let's work through a full (if simple) example to put the pieces together.
We'll write a dialog that lets a user add two numbers, and we'll
use that dialog to fill in a field on the original page.
First, let's wr
ite the dialog:
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:afh="http://xmlns.oracle.com/adf/faces/EA14/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/EA14" >
<jsp:directive.page contentType="text/html;charset=iso-8859-1"/>
<f:view>
<afh:html>
<afh:head title="Add dialog"/>
<afh:body>
<af:form>
<af:panelPage title="Add two numbers">
<f:facet name="messages">
<af:messages/>
</f:facet>
<!-- Two input fields -->
<af:panelForm>
<af:inputText label="Number 1:" value="#{chooseInteger.value1}"
required="true" tip="Enter an integer."/>
<af:inputText label="Number 2:" value="#{chooseInteger.value2}"
required="true" tip="Enter an integer."/>
</af:panelForm>
<!-- Two buttons -->
<f:facet name="actions">
<af:panelButtonBar>
<af:commandButton text="Submit" action="#{chooseInteger.select}"/>
<af:commandButton text="Cancel" immediate="true"
action="#{chooseInteger.cancel}"/>
</af:panelButtonBar>
</f:facet>
</af:panelPage>
</af:form>
</afh:body>
</afh:html>
</f:view>
</jsp:root>
This is a pretty simple page, with a couple of required input fields
and a couple of command buttons. We've put them in a bunch of
containers to make the page a bit prettier, but that's all fairly
simple. Now, we need to build the "chooseInteger" backing bean.
First, we'll add a managed-bean to our faces-config.xml, and also a
"dialog:" navigation rule so other pages can get to this dialog:
<managed-bean>
<managed-bean-name>chooseInteger</managed-bean-name>
<managed-bean-class>
oracle.adfdemo.view.faces.dialog.ChooseIntegerBean
</managed-bean-class>
<managed-bean-scope>
request
</managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/*</from-view-id>
<navigation-case>
<from-outcome>dialog:chooseInteger</from-outcome>
<to-view-id>/demos/chooseInteger.jspx</to-view-id>
</navigation-case>
</navigation-rule>
And now, the code for
ChooseIntegerBean
:
package oracle.adfdemo.view.faces.dialog;
import oracle.adf.view.faces.context.AdfFacesContext;
public class ChooseIntegerBean
{
public Integer getValue1()
{
return _value1;
}
public void setValue1(Integer value1)
{
_value1 = value1;
}
public Integer getValue2()
{
return _value2;
}
public void setValue2(Integer value2)
{
_value2 = value2;
}
public String cancel()
{
AdfFacesContext.getCurrentInstance().returnFromDialog(null, null);
return null;
}
public String select()
{
Integer value = new Integer(getValue1().intValue() +
getValue2().intValue());
AdfFacesContext.getCurrentInstance().returnFromDialog(value, null);
return null;
}
private Integer _value1;
private Integer _value2;
}
This code shows an example of how to call
returnFromDialog()
. In the cancel() case, we just return
null; for select(), we add the two numbers and return that Integer.
(We marked both fields as required, which is why I can be lazy and
fail to check that getValue1() and getValue2() aren't null.) But note
that in neither action do we have any notion of where we're returning
this value to - we just rely on the ADF Faces framework to figure
that out.
Now, let's write a page that uses that dialog:
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:afh="http://xmlns.oracle.com/adf/faces/EA14/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/EA14" >
<jsp:directive.page contentType="text/html;charset=iso-8859-1"/>
<f:view>
<afh:html>
<afh:head title="Dialog Demo"/>
<afh:body>
<af:form>
<!-- The field for the value; we point partialTriggers
at the button to ensure it gets redrawn when we return -->
<af:inputText label="Pick a number:" value="(Empty)"
partialTriggers="buttonId"
binding="#{launchDialog.input}"/>
<!-- The button for launching the dialog: we've also
configured the width and height of that window -->
<af:commandButton text="Add" action="dialog:chooseInteger"
id="buttonId"
windowWidth="300" windowHeight="200"
partialSubmit="true" useWindow="true"
returnListener="#{launchDialog.returned}"/>
</af:form>
</afh:body>
</afh:html>
</f:view>
</jsp:root>
We've kept it pretty simple: a single input field, stored with a
"binding", and a button that shows the "chooseInteger" dialog with a
hardcoded "dialog:" outcome. About the only un-typical thing in this
page is that "returnListener". Here's the managed-bean entry:
<managed-bean>
<managed-bean-name>launchDialog</managed-bean-name>
<managed-bean-class>
oracle.adfdemo.view.faces.dialog.LaunchDialogBean
</managed-bean-class>
<managed-bean-scope>
request
</managed-bean-scope>
</managed-bean>
And, here's the
LaunchDialogBean
code:
package oracle.adfdemo.view.faces.dialog;
import oracle.adf.view.faces.component.UIXInput;
import oracle.adf.view.faces.event.ReturnEvent;
public class LaunchDialogBean
{
public UIXInput getInput()
{
return _input;
}
public void setInput(UIXInput input)
{
_input = input;
}
public void returned(ReturnEvent event)
{
if (event.getReturnValue() != null)
{
getInput().setSubmittedValue(null);
getInput().setValue(event.getReturnValue());
}
}
private UIXInput _input;
}
We include the methods used by "binding" to have access to the
input field, and a single
returned()
method which will be
called when the dialog finishes. If the return value is null, then
the user cancelled the dialog, and we don't need to do anything.
Otherwise, we need to set the value on the input component. It's not
quite enough to just call
setValue()
on the input
component. You also have to call
setSubmittedValue()
with null to clear out any originally submitted value.
Passing Information In and Out
You've already seen how to return a value from a dialog by
passing that value to
AdfFacesContext
's
returnFromDialog()
method. But how to pass
a value in? ADF Faces lets you supply your dialogs
with a Map of "dialog parameters". (If you haven't
read the
Communicating Between Pages: processScope
chapter, now
might be a good time.)
When ADF Faces knows that its about to launch a
dialog because of the outcome of an action method, it queues
a
LaunchEvent
. One of the methods one this event
is
getDialogParameters()
, which will give you a
Map that you can add parameters to:
<af:commandButton text="Get Value" action="dialog:getValue"
returnListener="#{backing.handleReturn}"
launchListener="#{backing.addParameter}"/>
...
public void addParameter(LaunchEvent event)
{
// Pass the current value of the field into the dialog.
Object value = getInput().getValue();
event.getDialogParameters().put("inputValue", value);
}
When you add parameters in this way, they show up in the dialog
on the ADF Faces "processScope", and pages in the dialog can
get the value out of the processScope:
<af:outputText value="Input value: #{processScope.inputValue}"/>
The ADF Faces processScope has a very specific interaction with
dialogs. The dialog always gets a copy of all
values that were in the processScope of the launching page, plus
all the "dialog parameters" in the LaunchEvent. But anything
you do to the processScope inside the dialog ends there - when
you return from the dialog, the processScope will be back
the way it was before the dialog started. If you need to
pass values out of the dialog, you have to use
AdfFacesContext.returnFromDialog()
, session scope,
or application scope. This approach means that dialogs
are isolated from the calling code, and gives dialogs
a place to store values without worrying about overwriting
something that the parent window needed.
selectInputText
One pattern is so common that we've given it its own component:
<
af:selectInputText
>
. This is an input component that can
launch a dialog and automatically accept its return value, so
instead of the prior example combining an
<
af:inputText
>
and
<
af:commandButton
>
, you simply need:
<af:selectInputText label="Pick a number:" value="(Empty)"
action="dialog:chooseInteger"
windowWidth="300" windowHeight="200"/>
This component wi
ll automatically handle launching the dialog,
receiving the
ReturnEvent
, and updating the value
of the field, all without any code on your part. (We don't
need "partialSubmit" or "useWindow"; those are automatically
set by
<
af:selectInputText
>
.)
Manually Launching A Dialog
You can also programatically launch a dialog without any
navigation rule using the
AdfFacesContext.launchDialog()
method:
/**
* Launch a dialog, optionally raising it in a new window.
*
* The dialog will receive a new processScope map,
* which includes all the values of the currently available processScope
* as well as a set of properties passed to this function in
* the dialogParameters map. Changes to this newly
* created scope will not be visible once the dialog returns.
*
* @param dialogRoot the UIViewRoot for the page being launched
* @param source the UIComponent that launched the dialog and
* should receive the ReturnEvent when the dialog is complete.
* @param dialogParameters a set of parameters to populate the
* newly created processScope
* @param windowProperties the set of UI parameters used to
* modify a popup window. The set of properties that are
* supported will depend on the RenderKit, but
* common examples include "width" and "height".
* @param useWindow if true, use a popup window for the dialog
* if available on the current user agent device
*/
public abstract void launchDialog(
UIViewRoot dialogRoot,
UIComponent source,
Map dialogParameters,
Map windowProperties,
boolean useWindow);
You can call this method from an action listener, or from a
value change listener, or anything else. For example, you might use
an ADF Faces
<
af:poll
>
component set to poll every
10 minutes to show a dialog warning the user that the session is abou
t
to expire:
<!-- Send a PollEvent after 10 minutes -->
<af:poll pollListener="#{backing.sessionAboutToExpire}"
interval="600000"/>
...
public void sessionAboutToExpire(PollEvent event)
{
FacesContext context = FacesContext.getCurrentInstance();
// Create the dialog UIViewRoot
ViewHandler viewHandler = context.getApplication().getViewHandler();
UIViewRoot dialog = context.createView(context, "/sessionExpiring.jspx");
HashMap properties = new HashMap();
properties.put("width", new Integer(250));
properties.put("height", new Integer(150));
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
afContext.launchDialog(dialog,
null, // not launched from any component
null, // no particular parameters
properties,
true); // show it in a dialog
}
}
Supporting Dialogs in Custom Components
The ADF Faces dialog framework can be used inside your own
components and renderers. You can use it in one of two ways.
First, in a component that already supports ActionEvents, you
can add direct support for LaunchEvents and ReturnEvents at
the component level. Less obviously, you can take advantage
of the dialog framework to let a Renderer launch a dialog
and handle return values without ever interfering with
the component. This latter technique is especially handy
if you want to bundle some dialogs with your renderer,
like a calendar dialog with a date-editing component.
First, let's look at the case where you already support ActionEvents
and are adding ReturnEvents and LaunchEvents to your component.
First, add code for ReturnListeners and LaunchListeners, which
looks like the code for all other Faces listeners:
public YourComponent implements ActionSource
{
...
public void addReturnListener(ReturnListener listener)
{
addFacesListener(listener);
}
public void removeReturnListener(ReturnListener listener)
{
removeFacesListener(listener);
}
public ReturnListener[] getReturnListeners()
{
return (ReturnListener[]) getFacesListeners(ReturnListener.class);
}
public MethodBinding getReturnListener()
{
return _returnListener;
}
public void setReturnListener(MethodBinding returnListener)
{
_returnListener = returnListener;
}
// and exactly the same thing for LaunchListeners...
public void addReturnListener(LaunchListener listener)
{
addFacesListener(listener);
}
// etc...
}
Next, we need to detect
ReturnEvents
. Here, we'll
use a method on an
DialogService
, which
you get from an
AdfFacesContext
:
getReturnEvent(UIComponent source)
:
public void decode(FacesContext context)
{
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
ReturnEvent event =
afContext.getDialogService().getReturnEvent(this);
if (event != null)
event.queue();
else
super.decode(context);
}
The
getReturnEvent()
method sees if there is a pending
ReturnEvent
for your component. If there is, you can
queue it and forget about any further decoding.
All the rest of the interesting code lives in broadcast(). It's
a lot of code, so we'll walk through it.
public void broadcast(FacesEvent event) throws AbortProcessingException
{
// Perform special processing for ActionEvents: tell
// the AdfFacesContext to remember this component instance
// so that the NavigationHandler can locate us to queue
// a LaunchEvent.
if (event instanceof ActionEvent)
{
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
afContext.getDialogService().setCurrentLaunchSource(this);
try
{
// Perform standard superclass processing
super.broadcast(event);
// Notify the specified action listener method (if any),
// and the default action listener
_invokeListener(event, getActionListener());
FacesContext context = getFacesContext();
ActionListener defaultActionListener =
context.getApplication().getActionListener();
if (defaultActionListener != null)
{
defaultActionListener.processAction((ActionEvent) event);
}
}
finally
{
afContext.getDialogService().setCurrentLaunchSource(null);
}
}
else
{
// Perform standard superclass processing
super.broadcast(event);
if (event instanceof LaunchEvent)
{
_invokeListener(event, getLaunchListener());
LaunchEvent launchEvent = (LaunchEvent) event;
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
boolean useWindow =
Boolean.TRUE.equals(getAttributes().get("useWindow"));
launchEvent.launchDialog(useWindow);
}
else if (event instanceof ReturnEvent)
{
_invokeListener(event, getReturnListener());
getFacesContext().renderResponse();
}
}
}
//
// Helper function for invoking a MethodBinding for a listener
//
private final void _invokeListener(
FacesEvent event,
MethodBinding method) throws AbortProcessingException
{
if (method != null)
{
try
{
FacesContext context = getFacesContext();
method.invoke(context, new Object[] { event });
}
catch (EvaluationException ee)
{
Throwable t = ee.getCause();
// Unwrap AbortProcessingExceptions
if (t instanceof AbortProcessingException)
throw ((AbortProcessingException) t);
throw ee;
}
}
}
broadcast() basically handles each type of event one-by-one: first
ActionEvents
, then
LaunchEvents
, and finally
ReturnEvents
. In all three cases, we call
super.broadcast()
,
which will call any listeners registered with an
addXyzListener()
method, and then call
_invokeListener()
to handle
the associated
MethodBinding
.
The rest of the
ActionEvent
handling is mostly code required by
JSF: in particular, we call through to the "default ActionListener". This
is the code that invokes the Action and calls the
NavigationHandler
. But, in addition, we use
AdfFacesContext.getDialogService().setCurrentLaunchSource()
. This call
is needed so that code from a
NavigationHandler
can correctly queue a
LaunchEvent
; to do that, it
needs the source UIComponent, and it does not typically
have access to the source component. This method gives it
that access. (If you do not call this method, you don't get
a
LaunchEvent
back, and the NavigationHandler will
simply launch a non-popup-window dialog directly.)
The
LaunchEvent
handling code - after letting listeners
get a crack to add parameters, etc. - uses a utility method
on the
LaunchEvent
to launch the dialog.
In this example, we're checking the "useWindow" attribute to see if
we want to use a window for the dialog. This isn't required at all - you
could use a different attribute, or always pass
true
, etc.; you
can pick any strategy you want for your component.
Finally, in ReturnEvent, we don't do much beyond letting
listeners detect the return, but we do call
FacesContext.renderResponse()
, because you
don't generally want to update the model immediately upon
returning from a dialog, but you're free to design your component
othe
rwise.
Supporting Dialogs in Custom Renderers
In a custom Renderer, there may be no
LaunchEvent
or
ActionEvent
, and there's not really a
ReturnEvent
either, since the UIComponent has to contain code for listeners
and to handle
broadcast()
. Even so, we can still
use the dialog framework to show a dialog from our renderer
and handle returning from the dialog, all without any code
from the page author using the renderer. We'll need only
two functions on
AdfFacesContext
,
both of which we've already seen:
launchDialog
and
getReturnEvent
. For an example, let's imagine we've
already get a Renderer for an input field, and we want to add add a
button that launches a dialog, and uses the value to fill in that
dialog.
<!-- An input field that uses /myDialog.jsp to get the value -->
<my:inputFromDialog viewId="/myDialog.jsp"/>
First, we'll add some
encodeEnd()
output to
put in a button:
public class InputFromDialogRenderer
{
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException
{
// ... any pre-existing encodeEnd() code
// Write out <input type="submit" name="[clientId]:button" value="dialog">
ResponseWriter out = context.getResponseWriter();
out.startElement("input", component);
out.writeAttribute("type", "submit", null);
String clientId = component.getClientId(context);
out.writeAttribute("name", clientId + ":button", null);
out.writeAttribute("value", "dialog", null);
out.endElement("input");
}
The rest of the code goes in decode(). It'll need to detect
both when we want to launch a dialog, and when we're returning
from a dialog:
public void decode(FacesContext context, UIComponent component)
{
// Get the EditableValueHolder interface, and get the
// AdfFacesContext
EditableValueHolder evh = (EditableValueHolder) component;
AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
// If there's a ReturnEvent waiting for us, just use its value
// and finish
ReturnEvent event =
afContext.getDialogService().getReturnEvent(component);
if (event != null)
{
evh.setSubmittedValue(event.getReturnValue());
// Don't try writing to the model - just redisplay the
// page with this value
context.renderResponse();
return;
}
Map parameters = context.getExternalContext().getRequestParameterMap();
String id = component.getClientId(context) + ":button";
// See if the user pressed a button
if ("dialog".equals(parameters.get(id)))
{
// Get the right viewId
String viewId = (String) component.getAttributes().get("viewId");
UIViewRoot viewRoot =
context.getApplication().getViewHandler().createView(context,
viewId);
// Pass the current value to the dialog
Map dialogParameters = new HashMap();
dialogParameters.put("inputValue", evh.getValue());
// And give it a width and height (hardcoded in this example)
Map windowProperties = new HashMap();
windowProperties.put("width", new Integer(300));
windowProperties.put("height", new Integer(200));
afContext.launchDialog(viewRoot,
this,
dialogParameters,
windowProperties,
true);
}
else
{
// Old code for decode() goes here..
...
}
}
First, we check for a
ReturnEvent
. Our component
here doesn't support
ReturnEvents
, so we're
not going to queue
this event. Instead, we'll just grab the
dialog's return value and call
setSubmittedValue()
to save it off. Otherwise, we see if the button was clicked,
and, if so, create a
UIViewRoot
and ask the
AdfFacesContext
to show it in a dialog. We're
also passing the current value of our input field off to
the dialog as a dialog parameter. And that's it: lots of ADF
Faces code will work behind the scenes to pop up your dialog,
but the Renderer code is simple, and the page author doesn't
need to do a thing - the input component will just deliver
an ordinary
ValueChangeEvent
the next time
the page is submitted.
Low-level APIs: pushView() and popView()
In part, the dialog code relies on a couple of methods on the
DialogService
API:
/**
* Push a UIViewRoot onto a stack in preparation
* for navigating to a dialog.
*/
public abstract void pushView(UIViewRoot viewRoot);
/**
* Pop a UIViewRoot from a stack. If navigateToPopped is true,
* this method may result in calls to
* FacesContext.renderResponse() or even
* FacesContext.responseComplete().
*
* @param navigateToPopped If true, navigate to the view popped
* of the stack with FacesContext.setViewRoot().
* If false, simply drop the view.
*/
public abstract void popView(boolean navigateToPopped);
When you use these, you can't get a
ReturnEvent
, and
you don't get any special support for
processScope
isolation, but if you don't need either of these, it can provide a
very handy way to, for example, jump to a login page:
public String doSomethingNeedingLogin()
{
if (!isUserLoggedIn())
{
// Push the current view root
FacesContext context = FacesContext.getCurrentInstance();
AdfFacesContext.getCurrentInstance().getDialogService().
pushView(context.getViewRoot());
// And log in
return "login";
}
else
{
...
}
}
Now, in our login page, we just need to return to where we came from:
public String logIn()
{
if (loginWasSuccessful())
{
// Pop back
AdfFacesContext.getCurrentInstance().getDialogService().
popView(true);
// And return null, because popView() already did the navigation
return null;
}
else
{
...
}
}
Dialog Caveats
Users of the ADF Faces dialog framework should be aware of a few
current limitations.
First, while we can detect that a particular user agent - like a PDA -
cannot launch windows, and fall back to single-window behaviors - we
cannot currently detect popup blockers (like the current
Mozilla/FireFox popup blocker) that may or may not be enabled on a
particular user. Supporting this would be rather tricky, because
popup blockers don't give the server any sort of advanced knowledge
that would let us know to switch our server-side behavior. The
current solution is, unfortunately, asking users to disable popup
blocking for your site. Long term, we might try detecting support for
popups up front, or some other hackery, and in the longer term, we may
move to using inline DIVs and IFRAMEs to show dialogs; one nice thing
about the dialog framework is that it would hide any such changes from
programmers.
Second, we do not currently support the use of
<
redirect/
>
in
association with navigation rules used to show a popup window. (The behavior
you'll see is that you navigate directly to the page instead of
showing a popup window.)
|
Copyright © 2003-2006, Oracle Corporation. All Rights Reserved.
|
|
|
|