Tapestry使用AJAX从可编辑网格更新DAO

时间:2013-08-07 12:49:02

标签: java ajax dynamic datagrid tapestry

我正在尝试在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和其他互联网上建议的很多东西,但它们似乎不起作用,我不知道为什么。

2 个答案:

答案 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;
    }
}