-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I've been attempting to create a page that allows for the selection of
"content", based simply on a single category name ("content" in this case has
a category, and what I want to do is show any "content" matching that
category).
The page itself contains a single component, called the ContentSelector.
The selector takes an input parameter, being a Collection of strings. It uses
this to produce a IPropertySelectionModel implementation, similar to that of
the StringPropertySelection, and then renders this as a drop down using the
PropertySelection component.
I am then attempting to store the 'currently selected' category as a String.
When it is changed, I choose some matching content objects from some back
end, and show these in a table using a MultiplePropertySelection component
with a custom renderer.
Most of this I think I understand. Up until the bit where it doesn't work :-)
Currently for testing, I'm using a simple static defined list of 'matching
content' (that is, it's hard coded into the class...), along with statically
defined content names - so there is no back end to speak of.
I can get the first page to render, and show a list of catgories. The
component is setup to auto-submit if you select something. When I change the
selection I get a stale link exception (I'm using the Action service, not
Direct. With Direct I get an NPE).
I think the reason is that I have misunderstood how to manage page state - and
what I can/can not do.
I've attached the page specification for the ContentSelector, as well as it's
template and Java file. I know this could be a lot to wade through, but I
figure it's going to be very difficult for anyone knowledgeable to be able to
help without this stuff.
My primary questions are:
1) when is a page considered stale, and how does Tapestry work this out?
2) am I going about all this in a very wrong way, in terms of 'best practice'
ways of doing thing with Tapestry?
I'm not looking for someone to just point out the problem and say "here 'tis",
althought that would be nice :-) - I'm quite happy to put work in to also try
to find it..... just now I'm stuck :-(.
- --
Regards,
Neil Clayton
(PS: If you see strange text you don't understand underneath my email, don't
worry - it's just my PGP signature)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)
iD8DBQE9pqdQLXcfQF3yrNoRApeFAJ99E8JxUHFKPhL5i/kMjiz+I1wE7QCdGS8v
lmhikznGJxIDIWoR9comxCw=
=aRmd
-----END PGP SIGNATURE-----
Select content to edit
package tcdc.dataarmour.website.components;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Category;
import net.sf.tapestry.BaseComponent;
import net.sf.tapestry.IRequestCycle;
import net.sf.tapestry.event.PageDetachListener;
import net.sf.tapestry.event.PageEvent;
import net.sf.tapestry.form.IPropertySelectionModel;
import net.sf.tapestry.form.StringPropertySelectionModel;
import tcdc.website.common.content.SimpleContent;
/**
* @author neil
*/
public class ContentSelector extends BaseComponent implements PageDetachListener {
/**
* @see net.sf.tapestry.event.PageDetachListener#pageDetached(PageEvent)
*/
public void pageDetached(PageEvent arg0) {
category = null;
categories = null;
categoryModel = null;
matchingContentModel = null;
matchingContent = null;
selectedContent = null;
}
// A FORM submission (but not a custom linked button).
// Redo the query
public void submit(IRequestCycle cycle) {
}
public void add(IRequestCycle cycle) {
}
public void edit(IRequestCycle cycle) {
}
public void delete(IRequestCycle cycle) {
}
/**
* This exists because of a bug in Eclipse/Spindle that was getting on my nerves, re it re-writting expressions
* to be non-XML friendly, and then complaining about it!
*/
public boolean isAnyMatchingContent() {
return getMatchingContent() != null && getMatchingContent().size() > 0;
// "matchingContent != null && matchingContent.size %gt; 0";
}
/**
* Returns the category.
* @return String
*/
public String getCategory() {
cat.debug("Returning category " + category);
return category;
}
/**
* Sets the category.
* @param category The category to set
*/
public void setCategory(String category) {
cat.debug("Setting category to " + category);
this.category = category;
cat.debug("Setup the content that matches category " + category);
List theMatchingContent = new ArrayList();
theMatchingContent.addAll(SimpleContent.getSampleContent());
setMatchingContent(theMatchingContent);
fireObservedChange("selected", category);
}
/**
* Returns the matchingContent.
* @return List
*/
public List getMatchingContent() {
cat.debug("Returning matching content " + matchingContent);
return matchingContent;
}
private void setMatchingContent(List matchingContent) {
cat.debug("Setting the matching content to " + matchingContent);
this.matchingContent = matchingContent;
matchingContentModel = new ContentSelectionModel(matchingContent);
cat.debug("Setting up the matching content model to " + matchingContentModel);
selectedContent = new ArrayList();
cat.debug("Cleared the selected content list");
}
/**
* Returns the matchingContentModel.
* @return Collection
*/
public IPropertySelectionModel getMatchingContentModel() {
cat.debug("Returning matching content model " + matchingContentModel);
return matchingContentModel;
}
/**
* Returns the selectedContent.
* @return List
*/
public List getSelectedContent() {
cat.debug("returning selected content " + selectedContent);
return selectedContent;
}
/**
* Sets the selectedContent.
* @param selectedContent The selectedContent to set
*/
public void setSelectedContent(List selectedContent) {
cat.debug("Setting selected content to " + selectedContent);
this.selectedContent = selectedContent;
}
/**
* Sets the categories.
* @param categories The categories to set
*/
public void setCategories(Collection categories) {
cat.debug("Setting categories to " + categories);
this.categories = new ArrayList();
if (categories != null) {
this.categories.addAll(categories);
this.categoryModel = new CategoryModel(this.categories);
cat.debug("Created new category model " + categoryModel);
}
// fireObservedChange("categories", categories);
}
/**
* Returns the categoryModel.
* @return IPropertySelectionModel
*/
public IPropertySelectionModel getCategoryModel() {
cat.debug("Returning category model " + categoryModel);
return categoryModel;
}
public Collection getCategories() {
cat.debug("Returning categories: " + categories);
return categories;
}
private String category;
private List categories;
private IPropertySelectionModel categoryModel;
private IPropertySelectionModel matchingContentModel;
private List matchingContent;
private List selectedContent;
private static Category cat = Category.getInstance(ContentSelector.class.getName());
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification
PUBLIC "-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd">
<!-- generated by Spindle, http://spindle.sourceforge.net -->
<component-specification class="tcdc.dataarmour.website.components.ContentSelector" allow-body="no" allow-informal-parameters="yes">
<parameter
name="categories"
java-type="java.util.Collection"
direction="in"
required="yes">
<description>
<![CDATA[ A string listing of Category names, which can be used to filter the content ]]>
</description>
</parameter>
<parameter
name="selected"
java-type="java.lang.String"
property-name="category"
direction="custom"
required="no">
<description>
<![CDATA[ This holds the selected category, that the user has chosen ]]>
</description>
</parameter>
<bean name="delegate" class="tcdc.dataarmour.website.pages.SimpleValidationDelegate" lifecycle="request"/>
<bean name="matchingRenderer" class="tcdc.dataarmour.website.pages.secure.ContentRenderer" lifecycle="request"/>
<component id="add" type="Submit">
<static-binding name="label">Add Content</static-binding>
<binding name="listener" expression='listeners.add'/>
</component>
<component id="anyMatching" type="Conditional">
<binding name="condition" expression='anyMatchingContent'/>
</component>
<component id="categorySelection" type="PropertySelection">
<binding name="model" expression='categoryModel'/>
<binding name="submitOnChange" expression='true'/>
<binding name="value" expression="category"/>
</component>
<component id="delete" type="Submit">
<static-binding name="label">Delete Content</static-binding>
<binding name="listener" expression='listeners.delete'/>
</component>
<component id="edit" type="Submit">
<static-binding name="label">Edit Content</static-binding>
<binding name="listener" expression='listeners.edit'/>
</component>
<component id="form" type="Form">
<binding name="delegate" expression='beans.delegate'/>
<binding name="direct" expression='false'/>
<binding name="listener" expression='listeners.submit'/>
</component>
<component id="matching" type="Contrib:MultiplePropertySelection">
<binding name="model" expression='matchingContentModel'/>
<binding name="renderer" expression='beans.matchingRenderer'/>
<binding name="selectedList" expression='selectedContent'/>
</component>
</component-specification>