Reputation: 33
I have a component for editing db entity. It has @CommitAfter on onSuccess(). When it is on the separate page it works fine. I click update button, it saves data and redirects to view page.
Now I want to reuse it on the list page. By clicking on item it should appear. After editing and clicking update button on the component it should save item to db and hide itself.
To achieve this I modified component so I can set zone id for its form. Put component inside zone on the list page, added links for each item with event onSelectItem, which sets zone id for component and returns body of the zone.
It did show component and I could edit fields and hit update button. It updated item but redirected whole page to view page. I tried to return null in onSuccess() – but in this case it didn’t save data to db and zone also wasn’t refreshed. I also tried call page class from a component by using @InjectPage and return page.zone.getBody() – this does reload zone but still doesn’t save data though all methods passed w/o exception. Also it too bad to call page specific code inside a component. My other custom ajax calls do save data to db in methods with @CommitAfter.
So my question what is the correct way of doing this in Tapestry?
ContactsList.tml
<html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">
<t:form>
... list of contacts linked to onSelectContact()
</t:form>
<t:zone id="editContact" t:id="editContactZone">
<t:if test="selectedContact">
<t:contactform t:id="contactForm" />
</t:if>
</t:zone>
</html>
ListPage.java
public class ContactsList{
@InjectComponent
private Zone editContactZone;
@InjectComponent("contactForm")
private ContactForm contactForm;
@Property
private Contact selectedContact;
public Object onSelectContact(@RequestParameter(value = "id", allowBlank = false) Integer id) {
selectedContact = getContactById(id);
if (selectedContact != null) {
contactForm.setContact(selectedContact);
contactForm.setFormZone(editContactZone.getClientId());
}
return editContactZone.getBody();
}
}
ContactForm.tml
<div xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter">
<t:form t:id="editContactForm" zone="prop:zone">
.... contact fields
</t:form>
</div>
ContactForm.java
public class ContactForm{
@InjectComponent("editContactForm")
private Form editContactForm;
@Property
private String zone;
@InjectPage
private ContactList listPage;
@InjectPage
private ViewContact viewContact;
@Property
protected Contact contact;
public void setContact(Contact contact) {
this.contact = contact;
}
public void setFormZone(String zone){
this.zone = zone;
}
@CommitAfter
public Object onSuccess() {
parseContact();
storeContact(); // calls DAO.store(contact)
return zone == null ? viewContact : listPage.onSelectContact(0); //don't like this in component code but at least it returns zone's body
}
}
Upvotes: 0
Views: 1432
Reputation: 28099
You could pass a Block component parameter to the ContactForm containing the markup to render after @OnSuccess.
Or perhaps a better separation of concerns is to fire an event in the ContactForm which bubbles up to the page? Eg:
ContactForm.java
@Inject ComponentResources resources;
@CommitAfter
public void onSuccess() {
storeContact();
resources.triggerEvent("contactSaved", new Object[] { contact }, null);
}
ContactsList.java
@Inject AjaxResponseRenderer ajaxRenderer;
@Inject Zone someZone;
@Property Contact contact;
public void onContactSaved(Contact contact) {
this.contact = contact;
ajaxRenderer.addRender(someZone);
}
Also, why are you using @RequestParameter? Why not use event context?
ContactsList.tml
<t:loop source="contacts" item="contact">
<t:eventlink event="selectContact" context="contact">Edit ${contact.name}</t:eventlink>
</t:loop>
ContactList.java
Block onSelectContact(Contact contact) {
// doStuff
}
Upvotes: 1