我有一个组件,可以在一个区域中使用,该区域由选择更新。
这大致是我的组件的样子:
<div>
<input
t:type="textfield"
t:id="integerPart"
class="integerPart ${cssClass}"/>
</div>
大致如下:
<t:zone t:id="myZone" id="myZone">
<t:mycomponent />
</t:zone>
当选择触发区域更新时,输入将正确更新为给定值。提交页面后,我对用户输入的值和数据库中的值运行一些深入的计算。当触发错误时,我会在 ValidationTracker 中记录该错误,页面将被刷新,错误会显示在我的页面上的全局标记中。
问题是当页面刷新并显示错误消息时,我的文本字段的值会丢失。原因是 tapestry 重写了区域内的 id:http://tapestry.apache.org/ajax-components-faq.html
以及 AbstractTextField 中的这两种实现方法:
@BeginRender
void begin(MarkupWriter writer)
{
String value = tracker.getInput(this);
// lots of code
}
@Override
protected void processSubmission(String controlName)
{
String rawValue = request.getParameter(controlName);
tracker.recordInput(this, rawValue);
// lots of code
}
processSubmit在提交页面时被调用,我的文本字段中的当前值存储在由id"integerPart_12a820cc40e"标识的映射中,而当重新加载页面以显示错误消息时再次打印时,组件在映射中查找键"integerPart"。这不会产生匹配项,文本字段将呈现为空(即值丢失)。
我认为这是 Tapestry 中的一个已知问题,应该有一个快速简便的解决方法。我现在正在以一种"不那么快速和简单"的方式解决这个问题,这感觉完全错误。
我扩展了 TextField,并使用 id 存储值:"integerPart"而不是"integerPart_12a820cc40e"。这是通过这个类完成的:
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.Field;
import org.apache.tapestry5.ValidationTracker;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.corelib.components.TextField;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.Request;
public class ZoneFriendlyTextField extends TextField {
@Inject
private Request request;
@Environmental
private ValidationTracker tracker;
// copied from Abstract Field - to be able to use it here
@Parameter(value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL)
private String localClientId;
@Override
protected void processSubmission(String controlName)
{
super.processSubmission(controlName);
String submittedValue = request.getParameter(controlName);
tracker.recordInput(new TextFieldInternalField(localClientId), submittedValue);
}
/**
* This class is only used since the controlName of DateFieldset in a zone returns with a random id and
* it is not possible to match against the right value when page-errors occur and we have to present
* the user with the user-entered values again.
* Should only be used for recording input in the tracker.
*/
private static class TextFieldInternalField implements Field {
String clientId;
private TextFieldInternalField(String clientId) {
this.clientId = clientId;
}
@Override
public String getClientId() {
return null;
}
@Override
public String getControlName() {
return clientId;
}
@Override
public String getLabel() {
return null;
}
@Override
public boolean isDisabled() {
return false;
}
@Override
public boolean isRequired() {
return false;
}
}
}
如果有人能想出更好的解决方案来解决这个问题,我将不胜感激! :-)
注意:在Lance Java的回复之后,我稍微尖锐了这个问题。他对我最初的问题给出了一个很好的答案 - 但我认为这对我没有帮助,因为我的文本字段位于一个可以在一个页面中多次使用的组件中。
如果您提供 id (clientId),tapestry 将使用该 ID 而不是生成自己的动态值。由于需要动态值,因此可能需要从容器传递 clientId 参数。注意:对于此方法,您需要在任何 ajax 操作(例如 eventlink)的上下文中传递 clientId。
MyComponent.Java
@Parameter(required=true, defaultPrefix="literal")
private String clientId;
MyComponent.tml
<input t:type="textfield" id="${clientId}" t:id="integerPart" class="integerPart ${cssClass}"/>
我的页面.tml
<t:mycomponent clientId="instance1" ... />
<t:mycomponent clientId="instance2" ... />
<t:loop source="1..10" value="current">
<t:mycomponent clientId="prop:current" ... />
</t:loop>