Tapestry 不允许动态组件?
Tapestry does not allow dynamic components?
使用 Tapestry 5.3.7 我需要创建包含多个动态块的页面。
示例页面 tml:
<t:form>
<t:loop source="chosenBlockIds" value="blockId">
<t:delegate to="convertBlockIdToBlockObject(blockId)" myBlockId="${blockId}"/>
</t:loop>
<t:block id="blockA">
several text fields and select components here with zones
<t:textfield t:id="text1" value="dtoMap(blockId).text1">...
<t:select t:id="select1" t:zone="zone1" value="dtoMap(blockId).select1" ...>
<t:zone t:id="zone1" id="zone1" ...>
<t:select t:id="select2" value="dtoMap(blockId).select2" ...>
</t:zone>
</t:block>
<t:block id="blockB">
several text fields and select components here with zones
</t:block>
<t:block id="blockC">
several text fields and select components here with zones
</t:block>
</t:form>
在上一页中,用户将从可能的块列表中选择要显示的块。每个块都可以选择多次,这意味着例如blockA 可以渲染两次。这意味着 blockId 不能简单地为 blockA,而必须是唯一的值,如 blockA_1、blockA_2。我已经设法在 chosenBlockIds 列表中创建了这样的值。在委托中,方法 convertBlockIdToBlockObject 将解析 blockId,例如from blockA_2 会得到blockA,而return blockA对应的Block对象。这一切正常,页面呈现正确。
每个组件的值都绑定到一个 DTO class,其中包含 text1、select1、select2 等字段。每个 DTO 实例都存储在页面 class 的 Map 中.地图键是blockId。
让我们假设用户选择了 blockA 2x。因为 blockA 在页面上出现了 2x,所以每个块和块中的所有组件都需要知道块的唯一 blockId。我尝试在委托上使用非正式参数 myBlockId,希望将值传播为渲染变量。但是 Tapestry 不允许使用渲染变量作为块中任何组件的输入。如何将blockId传递给block中的所有组件?
现在提交表单或使用Ajax时出现问题,blockA_2的值混合到blockA_1中,这就是要解决的问题。此外,select2 在一个区域中,并根据使用 Ajax 在 select1 中选择的值进行刷新。错误地,当我在 blockA_2 中的 select1 中选择一个值时,它将更新 blockA_1 中的 select2。 class 页中的代码包含已声明的字段 Zone zone1,当 select1 中的值发生更改时,使用 onValueChanged 方法它将 return zone1.getBody()。但是我需要zone1 2x,一个用于blockA_1,第二个用于blockA_2,但是因为zone被声明为字段,显然我不能动态声明zone字段。看起来像 Tapestry 中无法解决的问题?我需要两个 zone1 实例(动态创建),可能存储在另一个以 blockId 为键的地图中。
第class页:
@Property
@Persist
private String blockId;
@Property
@Persist
private Map<String, DTO> dtoMap;
@Inject
private Block blockA;
@Inject
private Block blockB;
@Inject
private Block blockC;
@Component
private Zone zone1;
public Object onValueChanged(EventContext context) {
String blockId = context.get(String.class, 1);
// I need to return zone1 per blockId, but I can't declare fields dynamically
return zone1.getBody();
}
你解释的是可行的,但你需要改变一些东西:
无论何时将表单字段放入循环中,都需要使用 t:SubmitNotifier, otherwise submitted values may be overridden from previous iteration. Remember, Tapestry has static structure. Example in Jumpstart http://jumpstart.doublenegative.com.au/jumpstart/examples/ajax/formlooptailored1
在 onValueChanged
中,不是按区域返回主体,而是使用 AjaxResponseRenderer#addRender(String clientId, Object renderer)
将具体块渲染到预定义的客户端区域,即:
if (request.isXHR())
{
ajaxResponseRenderer.addRender(getZoneIdFor(blockId), getBlock(blockId));
}
这里 getBlock()
可以 returns TML 中定义的实际块之一,请注意您的 t:Block
需要 t:id
才能被注入:
@Inject Block blockA;
@Inject Block blockB;
public getBlock(String blockId)
{
if (blockId.startsWith("blockA") return blockA;
// etc.
}
要实现 pt.2,您需要所有 t:Zone
组件都具有明确的 id="{getZoneIdFor(blockId)}"
,而不仅仅是 t:id
——它可以是任何字符串,但您需要确保它始终与您的 blockId
.
相同
使用 Tapestry 5.3.7 我需要创建包含多个动态块的页面。
示例页面 tml:
<t:form>
<t:loop source="chosenBlockIds" value="blockId">
<t:delegate to="convertBlockIdToBlockObject(blockId)" myBlockId="${blockId}"/>
</t:loop>
<t:block id="blockA">
several text fields and select components here with zones
<t:textfield t:id="text1" value="dtoMap(blockId).text1">...
<t:select t:id="select1" t:zone="zone1" value="dtoMap(blockId).select1" ...>
<t:zone t:id="zone1" id="zone1" ...>
<t:select t:id="select2" value="dtoMap(blockId).select2" ...>
</t:zone>
</t:block>
<t:block id="blockB">
several text fields and select components here with zones
</t:block>
<t:block id="blockC">
several text fields and select components here with zones
</t:block>
</t:form>
在上一页中,用户将从可能的块列表中选择要显示的块。每个块都可以选择多次,这意味着例如blockA 可以渲染两次。这意味着 blockId 不能简单地为 blockA,而必须是唯一的值,如 blockA_1、blockA_2。我已经设法在 chosenBlockIds 列表中创建了这样的值。在委托中,方法 convertBlockIdToBlockObject 将解析 blockId,例如from blockA_2 会得到blockA,而return blockA对应的Block对象。这一切正常,页面呈现正确。
每个组件的值都绑定到一个 DTO class,其中包含 text1、select1、select2 等字段。每个 DTO 实例都存储在页面 class 的 Map
让我们假设用户选择了 blockA 2x。因为 blockA 在页面上出现了 2x,所以每个块和块中的所有组件都需要知道块的唯一 blockId。我尝试在委托上使用非正式参数 myBlockId,希望将值传播为渲染变量。但是 Tapestry 不允许使用渲染变量作为块中任何组件的输入。如何将blockId传递给block中的所有组件?
现在提交表单或使用Ajax时出现问题,blockA_2的值混合到blockA_1中,这就是要解决的问题。此外,select2 在一个区域中,并根据使用 Ajax 在 select1 中选择的值进行刷新。错误地,当我在 blockA_2 中的 select1 中选择一个值时,它将更新 blockA_1 中的 select2。 class 页中的代码包含已声明的字段 Zone zone1,当 select1 中的值发生更改时,使用 onValueChanged 方法它将 return zone1.getBody()。但是我需要zone1 2x,一个用于blockA_1,第二个用于blockA_2,但是因为zone被声明为字段,显然我不能动态声明zone字段。看起来像 Tapestry 中无法解决的问题?我需要两个 zone1 实例(动态创建),可能存储在另一个以 blockId 为键的地图中。
第class页:
@Property
@Persist
private String blockId;
@Property
@Persist
private Map<String, DTO> dtoMap;
@Inject
private Block blockA;
@Inject
private Block blockB;
@Inject
private Block blockC;
@Component
private Zone zone1;
public Object onValueChanged(EventContext context) {
String blockId = context.get(String.class, 1);
// I need to return zone1 per blockId, but I can't declare fields dynamically
return zone1.getBody();
}
你解释的是可行的,但你需要改变一些东西:
无论何时将表单字段放入循环中,都需要使用 t:SubmitNotifier, otherwise submitted values may be overridden from previous iteration. Remember, Tapestry has static structure. Example in Jumpstart http://jumpstart.doublenegative.com.au/jumpstart/examples/ajax/formlooptailored1
在
onValueChanged
中,不是按区域返回主体,而是使用AjaxResponseRenderer#addRender(String clientId, Object renderer)
将具体块渲染到预定义的客户端区域,即:if (request.isXHR()) { ajaxResponseRenderer.addRender(getZoneIdFor(blockId), getBlock(blockId)); }
这里
getBlock()
可以 returns TML 中定义的实际块之一,请注意您的t:Block
需要t:id
才能被注入:@Inject Block blockA; @Inject Block blockB; public getBlock(String blockId) { if (blockId.startsWith("blockA") return blockA; // etc. }
要实现 pt.2,您需要所有
相同t:Zone
组件都具有明确的id="{getZoneIdFor(blockId)}"
,而不仅仅是t:id
——它可以是任何字符串,但您需要确保它始终与您的blockId
.