fresh
fresh

Reputation: 33

Tapestry 5.3: Component with form inside t:zone

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

Answers (1)

lance-java
lance-java

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

Related Questions