我正在尝试在Tapestry中完成以下工作。
我有Dictionary<String, Dictionary<String, Object>>
,其中包含我的数据。
我正在努力实现我有一个下拉菜单(选择组件),其中包含来自外部词典的键。
当该选择发生变化时,应使用所选子字典中的键和值更新网格。
例如:
Dictionary<String, Dictionary<String, Object>> dictionaries = new Hashtable<String, Dictionary<String, Object>>();
Dictionary<String, Object> dict1 = new Hashtable<String, Object>();
Dictionary<String, Object> dict2 = new Hashtable<String, Object>();
dict1.put("k1", "d1v1");
dict1.put("k2", "d1v2");
dict2.put("k1", "d2v1");
dict2.put("k2", "d2v2");
dictionaries.put("d1", dict1);
dictionaries.put("d2", dict2);
有人会如此友善地给我一个例子或推动我朝着正确的方向前进吗?
编辑:
我设法让第一部分工作,网格内容根据外部字典中的哪些键被选中而改变。
但是,我在更新网格和保存更改方面遇到了问题:
我正在使用自己的GridDataSource:
public class EntityDataSource<T> implements GridDataSource {
private Class<T> persistentClass;
protected List<T> data;
protected int count = -1;
protected int startIndex = 0;
public EntityDataSource(Class<T> persistentClass, List<T> data) {
this.persistentClass = persistentClass;
this.data = data;
}
public static final <T> EntityDataSource<T> create(
Class<T> persistentClass, List<T> data) {
return new EntityDataSource<T>(persistentClass, data);
}
public int getAvailableRows() {
return this.data.size();
}
public void prepare(int startIndex, int endIndex,
List<SortConstraint> sortConstraints) {
this.startIndex = startIndex;
this.data = this.data.subList(startIndex, endIndex + 1);
}
public Object getRowValue(int index) {
return this.data.get(index - this.startIndex);
}
@SuppressWarnings("rawtypes")
public Class getRowType() {
return this.persistentClass;
}
}
我的网格看起来像这样:
<t:form t:id="configSelectForm">
<t:select t:id="storageKeySelecter"
t:model="storageKeyModel"
t:value="storageKey"
zone="configZone" />
</t:form>
<t:zone t:id="configZone" id="configZone">
<t:form t:id="configReviewForm">
<table width="100%">
<t:grid t:source="configurationEntrySource"
t:add="update, delete"
t:row="configurationEntry"
t:mixins="DisableGridSorting"
t:include="configKey, configValue"
t:encoder="configurationEntryEncoder">
<p:configValueCell>
<input t:id="value" t:type="TextField" t:value="configurationEntry.configValue"
t:validate="required" t:context="configurationEntry.configValue" />
</p:configValueCell>
<p:updateCell>
<t:actionlink t:id="update" zone="configZone" context="[configurationEntry.configKey, configurationEntry.configValue]">Update</t:actionlink>
</p:updateCell>
<p:deleteCell>
<t:actionlink t:id="delete" zone="configZone" context="configurationEntry.configKey">Delete</t:actionlink>
</p:deleteCell>
</t:grid>
</table>
<br></br>
<!-- <input type="submit" value="Update" class="button" /> -->
</t:form>
</t:zone>
我设法使删除链接工作,但是,我似乎无法更新任意键的值。
当我点击要更新的链接时,文本字段中的值不是正在传递的值:
public Object onActionFromUpdate(String configKey, String configValue) {
// my stuff here
return request.isXHR() ? configZone.getBody() : null;
}
configValue是当前存在的条目的值,而不是我正在尝试将其更改为的条目。
有没有办法获得这个价值?我的网格有一个任意行数。
EDIT2:提供了更多信息
这是我的TML:
<t:form t:id="configSelectForm">
<t:select t:id="storageKeySelecter"
t:model="storageKeyModel"
t:value="storageKey"
zone="configZone" />
</t:form>
<br/>
<t:zone t:id="configZone" id="configZone" elementName="div">
<form t:type="form" t:id="configReviewForm" id="configReviewForm" t:zone="configZone" zone="configZone">
<table width="100%">
<t:grid t:source="configurationEntries"
t:add="update, delete"
t:row="configurationEntry"
t:mixins="DisableGridSorting"
t:include="configKey, configValue"
t:encoder="configurationEntryEncoder" >
<p:configValueCell>
<input t:id="configValueTextField" t:type="TextField" t:value="configurationEntry.configValue"
t:validate="required" />
</p:configValueCell>
<p:updateCell>
<t:actionlink id="update" t:id="update" zone="configZone" t:context="[configurationEntry.configKey, configurationEntry.configValue]">Update</t:actionlink>
</p:updateCell>
<p:deleteCell>
<t:actionlink id="delete" t:id="delete" zone="configZone" t:context="configurationEntry.configKey">Delete</t:actionlink>
</p:deleteCell>
</t:grid>
</table>
<br/>
<input type="submit" value="Update" class="button" zone="configZone"/>
</form>
</t:zone>
<br/>
<br/>
<t:form t:id="addNewEntryForm">
<table width="100%">
<tr>
<td>
<input t:id="newEntryKey" t:type="textfield" t:value="newEntry.configKey"
t:validate="required" t:context="newEntry.configKey" />
</td>
<td>
<input t:id="newEntryValue" t:type="textfield" t:value="newEntry.configValue"
t:validate="required" t:context="newEntry.configValue" />
</td>
<td>
<input type="submit" value="Add New Entry" zone="configZone"/>
</td>
</tr>
</table>
</t:form>
这是我的JAVA:
public class PropertyConfiguration {
@Inject
private BeanModelSource beanModelSource;
@Component
private Form configReviewForm;
@Property
private List<ConfigurationEntry> configurationEntries;
private List<ConfigurationEntry> persistentEntries;
@Property
private ConfigurationEntry configurationEntry = new ConfigurationEntry("", "");
@Property
private ConfigurationEntryEncoder configurationEntryEncoder;
@InjectComponent
private Zone configZone;
@InjectComponent
private TextField configValueTextField;
@Inject
private ConfigurationPersitanceDAO dao;
private GridDataSource dataSource;
@Inject
private Messages messages;
@Property
private ConfigurationEntry newEntry;
@Inject
private Request request;
@Property
@Validate("required")
@Persist(PersistenceConstants.SESSION)
private String storageKey;
private StringSelectModel storageKeysSelectModel;
public ValueEncoder<ConfigurationEntry> getConfigurationEntryEncoder() {
initConfigurationEntityEncoder();
return this.configurationEntryEncoder;
}
public BeanModel<ConfigurationEntry> getModel() {
return beanModelSource.createDisplayModel(ConfigurationEntry.class, messages);
}
public SelectModel getStorageKeyModel() {
if (storageKeysSelectModel == null) {
storageKeysSelectModel = new StringSelectModel(this.dao.getStorageKeys());
}
return storageKeysSelectModel;
}
private void initConfigurationEntityEncoder() {
if (this.configurationEntryEncoder == null) {
this.configurationEntryEncoder = new ConfigurationEntryEncoder(dao, storageKey);
}
}
private void initZoneData() {
if (this.storageKey == null) {
this.storageKey = this.dao.getStorageKeys().get(0);
}
initConfigurationEntityEncoder();
}
public Object onActionFromDelete(String configKey) {
System.out.println("Deleting from: " + storageKey + " entry: " + configKey);
this.dao.deleteConfigurationEntry(storageKey, configKey);
return request.isXHR() ? configZone.getBody() : null;
}
public Object onActionFromUpdate(String configKey, String configValue) {
this.dao.updateConfigurationEntry(storageKey, configKey, configValue);
return request.isXHR() ? configZone.getBody() : null;
}
void onActivate(String storageKey) {
initZoneData();
this.newEntry = new ConfigurationEntry("", "");
}
String onPassivate() {
this.newEntry = new ConfigurationEntry("", "");
return this.storageKey;
}
Object onRefresh() {
return request.isXHR() ? configZone.getBody() : null;
}
Object onSuccessFromAddNewEntryForm() {
String configKey = this.newEntry.getConfigKey();
String configValue = this.newEntry.getConfigValue();
this.dao.addConfigurationEntry(storageKey, configKey, configValue);
return request.isXHR() ? configZone.getBody() : null;
}
void onValidateFromAddNewEntryForm() {
return;
}
Object onValueChangedFromStorageKeySelecter(String storageKey) {
this.storageKey = storageKey;
initConfigurationEntityEncoder();
this.configurationEntries = wrap(this.dao.getConfiguration(storageKey));
return configZone.getBody();
}
private void updateChangedConfigurations(List<ConfigurationEntry> changedEntries) {
for (ConfigurationEntry changedEntry : changedEntries) {
String configKey = changedEntry.getConfigKey();
String configValue = changedEntry.getConfigValue();
System.out.println("Updated: [" + storageKey + ":" + configKey + ":" + configValue + "]");
this.dao.updateConfigurationEntry(storageKey, configKey, configValue);
}
}
void onValidateFromConfigReviewForm() {
this.persistentEntries = wrap(this.dao.getConfiguration(storageKey));
List<ConfigurationEntry> tmpList = new ArrayList<ConfigurationEntry>();
for (ConfigurationEntry newEntry : this.configurationEntries) {
for (ConfigurationEntry oldEntry : this.persistentEntries) {
System.out.println("NewEntry: " + newEntry.toString() + " vs. OldEntry: " + oldEntry.toString());
if (oldEntry.getConfigKey().equals(newEntry.getConfigKey())) {
if (!oldEntry.getConfigValue().equals(newEntry.getConfigValue())) {
newEntry.setConfigValue(newEntry.getConfigValue().trim());
tmpList.add(newEntry);
}
}
}
}
this.persistentEntries = tmpList;
}
Object onSuccessFromConfigReviewForm() {
updateChangedConfigurations(this.persistentEntries);
return request.isXHR() ? configZone.getBody() : null;
}
/**
* Wraps dictionary entries in instances of ConfigurationEntry
*/
private List<ConfigurationEntry> wrap(
Dictionary<String, Object> rawConfiguration) {
Set<String> keySet = new TreeSet<String>();
List<ConfigurationEntry> wrappedEntries = new ArrayList<ConfigurationEntry>();
Enumeration<String> keys = rawConfiguration.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
keySet.add(key);
}
for (String key : keySet) {
String value = (String) rawConfiguration.get(key);
ConfigurationEntry entry = new ConfigurationEntry(key, value);
wrappedEntries.add(entry);
}
return wrappedEntries;
}
}
由于某种原因,当单击“更新”操作链接时,传递给public Object onActionFromUpdate(String configKey, String configValue)
的值不是我刚刚写入指定文本域的值,而是在页面时从我的数据库中提取的值被渲染了。
例如,如果我最初有这些对:
key1 => value1
key2 => value2
key3 => value3
我希望将key2
的值更改为“newValue2”,传递给方法的参数是“key2”和“value2”而不是“key2”和“newValue2”。
同样的问题是大规模更新不起作用的原因。 configurationEntries
中的所有值都是数据库中的shapshot值,而不是当前在网格中写入的值。
所以,我的问题是如何使用AJAX为我的具体示例从可编辑网格更新我的数据库。我已经尝试过在SO和其他互联网上建议的很多东西,但它们似乎不起作用,我不知道为什么。
答案 0 :(得分:3)
您需要使用zone
参数。
从select component's javadoc查看SelectZoneDemo.tml和SelectZoneDemo.java。它给出了一个在选择更改时更新区域的示例。
对于更复杂的互动,您可能会对this
感兴趣答案 1 :(得分:2)
要从输入中获取实际值,您需要提交表单。当您使用ActionLink时,将使用其上下文中的值(此值在链接呈现期间保存在上下文中,无法在客户端更改而不进行黑客攻击)。
要让您的示例正常工作,您可以为每一行使用单独的表单。您还需要一些可以在表单之外工作的提交组件:
<t:zone t:id="configZone" id="configZone">
<table width="100%">
<t:grid t:source="configurationEntrySource"
t:add="update, delete"
t:row="configurationEntry"
t:mixins="DisableGridSorting"
t:include="configKey, configValue"
t:encoder="configurationEntryEncoder">
<p:configValueCell>
<t:form t:id="configReviewForm" zone="configZone">
<input t:id="value" t:type="TextField" t:value="configurationEntry.configValue"
t:validate="required" t:context="configurationEntry.configValue" />
</t:form>
</p:configValueCell>
<p:updateCell>
<t:customlinksubmit form="configReviewForm">Update</t:customlinksubmit>
</p:updateCell>
<p:deleteCell>
<t:actionlink t:id="delete" zone="configZone" context="configurationEntry.configKey">Delete</t:actionlink>
</p:deleteCell>
</t:grid>
</table>
<br/>
</t:zone>
CustomLinkSubmit:
@SupportsInformalParameters
public class CustomLinkSubmit implements ClientElement {
@Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.COMPONENT)
private Form form;
@Parameter(allowNull = false, defaultPrefix = BindingConstants.LITERAL)
private SubmitMode mode = SubmitMode.NORMAL;
@Inject
private ComponentResources resources;
@Inject
private JavaScriptSupport javascriptSupport;
private String clientId;
@BeginRender
void beginRender(MarkupWriter writer) {
clientId = javascriptSupport.allocateClientId(resources);
writer.element("span", "id", clientId);
resources.renderInformalParameters(writer);
}
@AfterRender
void afterRender(MarkupWriter writer) {
writer.end();
JSONObject spec = new JSONObject()
.put("form", form.getClientId())
.put("clientId", clientId)
.put("validate", mode == SubmitMode.NORMAL);
javascriptSupport.addInitializerCall(InitializationPriority.EARLY, "linkSubmit", spec);
}
public String getClientId() {
return clientId;
}
}