I am stuck in a rather weird situation.
I have a requirement in which i need a tree structure to define a list of elements. And when we click on any of those elements in a tree. it should expand the section below it to show 2 side by side datatables which are linked to that element.
I can use JSF 2.0 core or even primefaces 3.1.
Is it possible ??
Could anyone please help ... any suggestions would be appreciated.
Updated
My model is something like :
class Shop{
boolean isoperational;
String name;
List<Item> items;
List<boolean> itempresent;
List<Employee> employees;
}
I need the name of the shop on the tree node along with the isoperational checkbox;
And when we click on that node it should open 2 datatables.
One containing the List of items along with itempresent checkbox.
Other containing the List of employees.
Thanks
Edited per your response (if i understand you correctly). Note that this is written from head so some attribute names might differ. The point is to set an active collection from your treetable depending on which row is clicked. An ID is set via the actionListener, then the action sets mycontroller.activeCollection to the list you want to show. Then the datatable is rerendered via normal post or ajax with values depending on which row you clicked.
Markup
<p:treeTable value="#{myController.treenode}" var="item">
<p:column>
<h:commandLink action="#{myController.setActiveShop}" value="set">
<f:setPropertyActionListener target="#{myController.shipName}" value="#{item.shop.name}"/>
</h:commandButton>
</p:column>
</p:treeTable>
<p:dataTable value="#{myController.activeShop.items" rendered="#{!empty myController.activeShop}" var="item">
<p:column>#{item.name}</p:column>
</p:datatable>
<p:dataTable value="#{myController.activeShop.employees" rendered="#{!empty myController.activeShop}" var="item">
<p:column>#{item.name}</p:column>
</p:datatable>
Controller
#ManagedBean
#ViewScoped // Or #SessionScoped... something that can cache lists, not sure how this would be done with requestscope
public class MyController{
private List<Shop> shopList; // Set this from a datasource. Or use only shopTree below to store your shops..
private Shop activeShop;
private String shopName;
private TreeNode shopTree; // Generate this from shopList
// insert getters and setters
private setActiveShop(){
for(Shop s : shipList)
if(s.getName().equals(shopName)
activeShop = s;
}
}
Related
I have a datatable that has a collection of objects that is initialized on page load. It is not lazy loaded.
In this datatable row, I have a row expansion object that I wish to "toggle" depending on a boolean value for the object in that row (inputSwitch tag in Primefaces). Once that is toggled, I update the DOM and render the sub collection using the "rendered" attribute for the rowExpansion (which I suspect might be the problem, because I think that requires a DOM refresh)
However, whenever I hit the first inputSwitch (the main toggle), it reloads the entire collection and wipes out the changes that have been made.
<p:dataTable
id="myTable"
var="row"
reflow="true"
expandedRow="true"
filteredValue="#{mybean.filteredAllObjects}"
value="#{mybean.allObjects}">
<p:column>
<p:inputSwitch value="#{row.boolSelected}">
<p:ajax update=":myform:myTable"/>
</p:inputSwitch>
</p:column>
<p:rowExpansion rendered="#{row.boolSelected}">
<ui:repeat value="#{row.subsetCollection}" var="subsetRow">
<p:inputSwitch value="#{subsetRow.boolSelected}"/>
</ui:repeat>
</p:rowExpansion>
</p:dataTable>
How can I essentially keep the state of the collection on the client side without the need to re-render the collection (and thus eliminating all changes)?
Edit
Backing bean (simplified - it's really this simplistic). When the ajax call is made in the front end, the getter for the collection is called (breakpoint confirms this). Also see a POST is happening, so a call to the server is made:
#Component
#ViewScoped
public class MyBean implements Serializable
{
private static final long serialVersionUID = 1L;
private List<MyObject> allObjects;
private List<MyObject> filteredAllObjects;
public MyBean(#Value("#{param['id']}") String selectedId)
{
init();
}
private void init()
{
allObjects = ... // call to get objects from DB
}
// Setters & Getters
}
I'm currently using filters with options list on my lazy loaded DataTables. Everything works just fine except I'd like to be able to reload my filter options depending on currently selected filters.
For example, my DataTable has two fields country and region, and I want to filter
DataTable snippet :
<p:dataTable var="d" widgetVar="personneContactTable" value="#{bean.dataModel}" id="myDataTable" lazy="true">
<p:column sortBy="country" filterBy="country" filterOptions="#{bean.getCountryOptions()}">
[...]
</p:column>
<p:column sortBy="region" filterBy="region" filterOptions="#{bean.getRegionOptions()}">
[...]
</p:column>
</p:dataTable>
And my bean :
#ManagedBean(name = "bean")
#SessionScoped
public class MyBean implements Serializable {
LazyDataModel<MyStuff> dataModel;
String country;
String region;
public SelectItem[] getCountryOptions() {
return service.someMagic();
}
public SelectItem[] getRegionOptions() {
return service.someMoreMagic(country);
}
// + getters, setters, etc.
}
I tried using properties instead of methods, adding <p:ajax event="filter"> tags to try reloading part of the DataTable, but nothing worked. I found http://forum.primefaces.org/viewtopic.php?f=3&t=38087 too, but again not workable solution emerged.
How can I automaticaly refresh my filterOptions? (this is using Primefaces 4 ELITE branch)
I have a primefaces datatable and i have a column with filter.i would like to apply filter on the column from the backing bean.
I followed this example and i am able to get the input given filter text box into my bean.
but when i use setFilter ,the values are being set in the HashMap but filter is not being applied on the datatable.
Example column
<p:column filterBy="#{var.value}" headerText="Example" footerText="contains" filterMatchMode="contains" />
Bean is session scoped and the following code is in a function which gets called on a button click.
Map<String,String> theFilterValues = new HashMap<String,String>();
theFilterValues.put("filterColumn","someValue");
myDataTable.setFilters(theFilterValues);
this sets the values ,but there is no change on datatable.
i tried this but it did not help.
All i need is to set a filter on the datatable column upon a button click.
Thanks in advance
The values in the inputs of the DataTable filter are sent in the FacesContext request parameter map, and obtained by the DataTableRenderer when it is rendering the DataTable (see the encodeFilter method for PF 3.5, PF 4.0, or PF 6.1)
So, if your button is in the same form of the DataTable, the values of the filter are sent in the request parameter map, and the renderer will show those values over whatever else you want.
You'll need the button to be in a separate form:
<h:form>
<p:commandButton action="#{someBean.action()}" update="#([id$=dataTable])" />
</h:form>
<h:form>
<p:dataTable id="dataTable" [...] >
<p:column filterBy="#{var.col}" filterValue="#{someBean.filterValue}">
<h:outputText value="#{var.col}">
</p:column>
</p:dataTable>
</h:form>
And then, you can change the filterValue in the bean:
#Named
#SessionScoped
public SomeBean implements Serializable {
private String filterValue;
[...]
public void action() {
filterValue = "new value";
}
[getters/setters]
}
You can use a Map for the filterValues if you are using many filters.
As an alternative, if you need to redirect the user to a new page, you can put the values in the URL, instead of using filterValue. Example:
https://example.com/app/pageOfTheTable.xhtml?form:dataTable:colum:filter=new%20value
The part form:dataTable:colum:filter is the ID of the filter input. You can get that by inspecting the element using your browser. The principle is the same: you are using the request parameter map.
It may be useful to update the value of the backing bean when the user types something. I've found a patch here.
It basically changes populateFilterParameterMap method, in FilterFeature class, so it sets the value of the filterValue. You can put the added lines at the end of the for loop.
for ( ... ){
[...]
+ ValueExpression filterValueVE = column.getValueExpression("filterValue");
+ if (filterValueVE == null) {
+ ((UIComponent)column).getAttributes().put("filterValue", filterValue);
+ } else {
+ filterValueVE.setValue(context.getELContext(), filterValue);
+ }
}
Hi Have this Wierd Issue in which I am using a Composite Component which I wrote and I get values from the previous use of the backing bean of the CC (the componentType bean)
I don't know how to describe this better than just show the code.
I'll try to be brief about it and cut the redundant parts:
This is the Composite Component definition:
<cc:interface componentType="dynamicFieldGroupList">
<cc:attribute name="coupletClass" />
<cc:attribute name="form" default="#form"/>
<cc:attribute name="list" type="java.util.List" required="true"/>
<cc:attribute name="fieldNames" type="java.util.List" required="true" />
</cc:interface>
<cc:implementation>
<h:dataTable value="#{cc.model}" var="currLine">
<h:column>
<h:outputText id="inner_control_component" value="Inner Look at currLine:#{currLine}"/>
</h:column>
</h:dataTable>
</cc:implementation>
The CC bean defintion:
#FacesComponent(value = "dynamicFieldGroupList")
// To be specified in componentType attribute.
#SuppressWarnings({ "rawtypes", "unchecked" })
// We don't care about the actual model item type anyway.
public class DynamicFieldGroupList extends UIComponentBase implements
NamingContainer
{
private transient DataModel model;
#Override
public String getFamily()
{
return "javax.faces.NamingContainer"; // Important! Required for
// composite components.
}
public DataModel getModel()
{
if (model == null)
{
model = new ListDataModel(getList());
}
return model;
}
private List<Map<String, String>> getList()
{ // Don't make this method public! Ends otherwise in an infinite loop
// calling itself everytime.
return (List) getAttributes().get("list");
}
}
And the use code:
<ui:repeat var="group" value="#{currentContact.detailGroups}">
<h:panelGroup rendered="#{not empty group.values}">
<h:outputText id="controlMsg" value=" list:#{group.values}" /><br/><br/>
<utils:fieldTypeGroupList list="#{group.values}"
fieldNames="#{group.fields}" coupletClass="utils" />
</h:panelGroup>
</ui:repeat>
The text of id controlMsg displays the correct values in #{group.values} while the control output inside the component of id inner_control_component shows the values from the previous use.
The values are correct the first time...
I guess it's a fundemental error in use of a CC bean, otherwise it could be a bug with MyFaces 2.1 (Which I'm using)
The explanation for this behaviour is simple: there's only one component definied in the view. So there's also only one backing component with one model. Since the model is lazily loaded on first get, the same model is been reused in every iteration of a parent iterating component.
The <ui:repeat> doesn't run during view build time (as JSTL does), but during view render time. So there are physically not as many components in the view as items which are iterated by <ui:repeat>. If you were using <c:forEach> (or any other iteration tag which runs during view build time), then the composite component would have behaved as you'd expect.
You would like to change the way how the datamodel is kept in the backing component. You would like to preserve a separate datamodel for each iteration of a parent iterating component. One of the ways is to replace the model property as follows:
private Map<String, DataModel> models = new HashMap<String, DataModel>();
public DataModel getModel() {
DataModel model = models.get(getClientId());
if (model == null) {
model = models.put(getClientId(), new ListDataModel(getList()));
}
return model;
}
See also:
What's the view build time?
The problem described here is an old known isse in JSF, hidden by the usage of composite components. It is so important and so difficult, that instead answer here I create a detailed answer in a blog entry for this one: JSF component state per row for datatables
To keep this answer short, I'll say to you it is not a bug in MyFaces 2.1. Please use 2.1.1, because that is a quick bug fix version of 2.1.0. In JSF 2.1 there is a new property for h:dataTable called rowStatePreserved, and this scenario is just one case where "this little baby" becomes useful. Just replace ui:repeat with h:dataTable and add rowStatePreserved="true". That will do the trick. If you need to manipulate the model (add or remove rows) you can use tomahawk t:dataTable and t:dataList, but you will have to take an snapshot version for now. Note this is new stuff not available in any different JSF framework an the moment (JUN 2011).
If you need more info, keep tuned with MyFaces Team on Twitter or ask to the experts on MyFaces Users and Dev Mailing Lists.
I have a managed bean under ViewScope. It has an instance variable inside it.
MetaData object has a inputItem object List.
#ManagedBean
#ViewScoped
public class ConBean implements Serializable {
private MetaData metadata;
#PostConstruct
#SuppressWarnings("unchecked")
public void init() throws IOException {
this.metadata = new MetaData ();
}
public void proc(){
List<InputItem> inputs= new ArrayList<InputItem>();
inputs.add(***** code to populate the inputItem List);
//after populating, inputs added to the metadata
metadata.setInputs(inputs);
}
//getters & setters
}
in my JSF , input list is populated inside a UI repeat.
<div id="inputplaceholder">
<ui:repeat value="#{conBean.metaData.inputs}" var="content">
</ui:repeat>
</div>
the div inputplaceholder is periodically updated using a richfaces poll.
<a4j:poll id="poll" interval="12000" action="#{conBean.proc}"
execute="#form" render="inputplaceholder"/>
The problem that I have is even though inputItems are set to the metaData object correctly inside the proc() method, when the view is rendered/partially updated, it doesn't get highlighted in the UI. so partial update takes no effect. I tried moving
this.metadata = new MetaData ();
inside the proc method but had no luck.
any ideas and help is highly appreciated.
thanks ...
Did the partial render really take place? This is impossible. There is namely no JSF component with the ID inputplaceholder. You assigned it to a plain HTML <div> element. Replace it by a fullworthy JSF component:
<h:panelGroup layout="block" id="inputplaceholder">
Also, since you used a relative ID in the render attribute, it will only scan for components in the same parent naming container component. The <ui:repeat> is such one, however the component with the desired ID is placed outside it. You'd like to use an absolute ID instead. Assuming that it's inside a <h:form> with a fixed ID:
<h:form id="myform">
<h:panelGroup layout="block" id="inputplaceholder">
...
then you should be referencing it in the render attribute as follows
render=":myform:inputplaceholder"