NOTE: This is outdated information that applies only to Tapestry 4. |
PopupLinkSubmit
This is a solution for submitting a form and at the same time open a popup with an external link. It is a little lousy since it requires alot of copy paste from Tapestry code. I hope this can look better with future Tapestry versions.
Example
<a href="#" jwcid="@PopupLinkSubmit" parameters="ognl:article" listener="listener:acheterArticle" popuppage="PopupPanier" popupparameters="ognl:article.AR_Design">Acheter</a>
PopupLinkSubmit.jwc
<!DOCTYPE component-specification PUBLIC "-//Apache Software Foundation//Tapestry Specification 4.0//EN" "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">
<component-specification class="actualis.web.tapestry.components.PopupLinkSubmit" allow-body="yes" allow-informal-parameters="yes" deprecated="no">
<description>
Creates a hyperlink that submits its enclosing form using JavaScript.
</description>
<parameter name="disabled"/>
<parameter name="selected"/>
<parameter name="tag"/>
<parameter name="id" property="idParameter" default-value="id"/>
<parameter name="listener">
<description>
A listener that is notified if this component is triggered.
</description>
</parameter>
<parameter name="action">
<description>
A listener that is notified if this component is triggered
just before the form's listener, after all components
enclosed by the Form have had a chance to update their properties.
</description>
</parameter>
<parameter name="parameters">
<description>
An object, or list of objects, gathered when the link is triggered and
made available as listener parameters in the request cycle, making
the parameters available to a deferred listener.
</description>
</parameter>
<parameter name="popuppage" property="popupPage" required="yes"/>
<parameter name="popupparameters" property="popupParameters" required="no"/>
<reserved-parameter name="name"/>
<reserved-parameter name="href"/>
<inject property="listenerInvoker" object="infrastructure:listenerInvoker"/>
<inject property="script" type="script" object="PopupLinkSubmit.script"/>
</component-specification>
PopupLinkSubmit.script
<?xml version="1.0"?>
<!DOCTYPE script PUBLIC
"-//Apache Software Foundation//Tapestry Script Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Script_3_0.dtd">
<script>
<input-symbol key="name" class="java.lang.String" required="yes"/>
<input-symbol key="popupLink" class="java.lang.String" required="yes"/>
<input-symbol key="form" class="org.apache.tapestry.IForm" required="yes"/>
<let key="href">
javascript:submitPopupLink('${form.name}', '${name}','${popupLink}');
</let>
</script>package actualis.web.tapestry.components;
import java.util.HashMap;
import java.util.Map;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.service.BodyBuilder;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.IForm;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.IScript;
import org.apache.tapestry.PageRenderSupport;
import org.apache.tapestry.TapestryUtils;
import org.apache.tapestry.annotations.InjectObject;
import org.apache.tapestry.engine.ExternalServiceParameter;
import org.apache.tapestry.engine.IEngineService;
import org.apache.tapestry.engine.ILink;
import org.apache.tapestry.form.FormConstants;
import org.apache.tapestry.link.DirectLink;
import actualis.web.tapestry.components.form.FormMessages;
/**
* Implements a component that submits its enclosing form via a JavaScript link. [<a
* href="../../../../../ComponentReference/LinkSubmit.html">Component Reference</a>]
*
* @author Richard Lewis-Shell
* @version $Id$
*/
public abstract class PopupLinkSubmit extends CustomSubmit {
/**
* The name of an {@link org.apache.tapestry.IRequestCycle}attribute in which
* the current submit link is stored. LinkSubmits do not nest.
*/
public static final String ATTRIBUTE_NAME = "actualis.web.tapestry.PopupLinkSubmit";
public static final String ATTRIBUTE_NAME_SCRIPT = "actualis.web.tapestry.PopupLinkSubmitScript";
/**
* The name of an {@link org.apache.tapestry.IRequestCycle}attribute in which
* the link submit component that generates the javascript function is stored.
* The function is only required once per page (containing a form with a
* non-disabled LinkSubmit)
*/
public static final String ATTRIBUTE_FUNCTION_NAME = "actualis.web.tapestry.PopupLinkSubmit_function";
@InjectObject("engine-service:external")
public abstract IEngineService getExternalService();
/**
* Checks the submit name ({@link FormConstants#SUBMIT_NAME_PARAMETER}) to
* see if it matches this LinkSubmit's assigned element name.
*/
protected boolean isClicked(IRequestCycle cycle, String name) {
String value = cycle.getParameter(FormConstants.SUBMIT_NAME_PARAMETER);
return name.equals(value);
}
public ILink getLink(IRequestCycle cycle) {
Object[] pageParameters = DirectLink.constructServiceParameters(getPopupParameters());
ExternalServiceParameter esp = new ExternalServiceParameter(getPopupPage(),
pageParameters);
return getExternalService().getLink(false, esp);
}
public abstract Object getPopupParameters();
public abstract String getPopupPage();
/**
* @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter,
* org.apache.tapestry.IRequestCycle)
*/
@Override
protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) {
boolean disabled = isDisabled();
if (!disabled) {
PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle,
this);
if (cycle.getAttribute(ATTRIBUTE_NAME_SCRIPT) == null) {
BodyBuilder builder = new BodyBuilder();
builder.addln("function {0}(form, elementId, url)", "submitPopupLink");
builder.begin();
builder.addln("var form = document.getElementById(form);");
builder.addln("form.events.submit(elementId);");
builder.addln("aWindow = window.open(url, '" + PopupDirectLink.POPUP_WINDOWNAME
+ "', " + PopupDirectLink.POPUP_FEATURES + ", false);");
builder.addln("aWindow.focus();");
builder.end();
pageRenderSupport.addBodyScript(builder.toString());
cycle.setAttribute(ATTRIBUTE_NAME_SCRIPT, this);
}
IForm form = getForm();
String slink = getLink(cycle).getURL(null, true);
Map symbols = new HashMap();
symbols.put("form", form);
symbols.put("name", getName());
symbols.put("popupLink", slink);
getScript().execute(cycle, pageRenderSupport, symbols);
writer.begin("a");
writer.attribute("href", (String) symbols.get("href"));
}
renderBody(writer, cycle);
if (!disabled)
writer.end();
}
public abstract IScript getScript();
/**
* @see org.apache.tapestry.AbstractComponent#prepareForRender(org.apache.tapestry.IRequestCycle)
*/
protected void prepareForRender(IRequestCycle cycle) {
IComponent outer = (IComponent) cycle.getAttribute(ATTRIBUTE_NAME);
if (outer != null)
throw new ApplicationRuntimeException(FormMessages
.linkSubmitMayNotNest(this, outer), this, getLocation(), null);
cycle.setAttribute(ATTRIBUTE_NAME, this);
}
/**
* @see org.apache.tapestry.AbstractComponent#cleanupAfterRender(org.apache.tapestry.IRequestCycle)
*/
protected void cleanupAfterRender(IRequestCycle cycle) {
cycle.removeAttribute(ATTRIBUTE_NAME);
}
/**
* Links can not take focus, ever.
*/
protected boolean getCanTakeFocus() {
return false;
}
/**
* Returns true; the LinkSubmit's body should render during a rewind, even if
* the component is itself disabled.
*/
protected boolean getRenderBodyOnRewind() {
return true;
}
}AbstractSubmit has package visibility! So you need to copy/paste the whole thing.
package actualis.web.tapestry.components;
import java.util.Collection;
import org.apache.tapestry.IActionListener;
import org.apache.tapestry.IForm;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.form.AbstractFormComponent;
import org.apache.tapestry.listener.ListenerInvoker;
/**
* Copy of AbstractSubmit
*/
public abstract class CustomSubmit extends AbstractFormComponent {
/**
* Determine if this submit component was clicked.
*
* @param cycle
* @param name
* @return true if this submit was clicked
*/
protected abstract boolean isClicked(IRequestCycle cycle, String name);
/**
* @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle)
*/
protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle)
{
if (isClicked(cycle, getName()))
handleClick(cycle, getForm());
}
void handleClick(final IRequestCycle cycle, IForm form)
{
if (isParameterBound("selected"))
setSelected(getTag());
final IActionListener listener = getListener();
final IActionListener action = getAction();
if (listener == null && action == null)
return;
final ListenerInvoker listenerInvoker = getListenerInvoker();
Object parameters = getParameters();
if (parameters != null)
{
if (parameters instanceof Collection)
{
cycle.setListenerParameters(((Collection) parameters).toArray());
}
else
{
cycle.setListenerParameters(new Object[]
{ parameters });
}
}
// Invoke 'listener' now, but defer 'action' for later
if (listener != null)
listenerInvoker.invokeListener(listener, CustomSubmit.this, cycle);
if (action != null) {
Runnable notify = new Runnable()
{
public void run()
{
listenerInvoker.invokeListener(action, CustomSubmit.this, cycle);
}
};
form.addDeferredRunnable(notify);
}
}
/** parameter */
public abstract IActionListener getListener();
/** parameter */
public abstract IActionListener getAction();
/** parameter */
public abstract Object getTag();
/** parameter */
public abstract void setSelected(Object tag);
/** parameter */
public abstract boolean getDefer();
/** parameter */
public abstract Object getParameters();
/** Injected */
public abstract ListenerInvoker getListenerInvoker();
}