I have a basic JSF question. I have a loop where I am trying to create mutile command link depending on the list value. and that command link will call the corresponding action from the list filed.
Basically I have this bean:
public class FavoriteTasks implements Serializable {
private static final long serialVersionUID = -8702569738872927728L;
private String key;
private String action;
private String widget;
private String name;
public FavoriteTasks(String key, String action, String widget, String name) {
super();
this.key = key;
this.action = action;
this.widget = widget;
}
And then populating it using properties file:
private void setUpFavTasks(UserUIPreferencesVO uiPref) {
List<String> fTaskList = uiPref.getFavoriteTasks();
favTasks =new ArrayList<FavoriteTasks>();
for(String var:fTaskList){
FavoriteTasks ft = new FavoriteTasks(var,
ConfigurationData.getValue(var+".action"),
ConfigurationData.getValue(var+".widget"),
ConfigurationData.getValue(var+".name"));
favTasks.add(ft);
}
}
Now the issue is the action is not understanding that it needs to get the value first and read that and then make the method call depending on the value.
<ui:repeat value="#{userSessionBean.favTasks}" var="favTasks" >
<li><ice:commandLink styleClass="shortcut-menu" action="#{favTasks.action}">
<f:param name="filterByContentWidget" value="#{favTasks.widget}" />
<f:param name="filterByContentGroup" value="#{favTasks.key}" />
<f:param name="menuName" value="#{favTasks.name}" />
<h:outputText value="#{msgs[favTasks.key]}" />
</ice:commandLink>
</li>
</ui:repeat>
action is trying to get favTasks.action and failing as there are no such method. it needs to read the value stored in favTasks.action and then go to the method that value is saying... for example if the favTasks.action = catalogHandler.showCatalog. it needs to invoke catalogHandler.showCatalog not favTasks.action
The action attribute is used to indicate the next view when you click the commandLink. It is a method expression that returns a String.
For example:
public String method() {
//do something
return "success";
}
and in your commandLink as
<ice:commandLink value="Submit" action="#{bean.method}" />
when clicked will take you to success.xhtml
Also you need to declare public setters/getters you just can't get/set any private variables that you have in your code:
private String key;
private String widget;
private String name;
Related
I would like to pass an value to a managed bean under the hood. So I have this managed bean:
#ManagedBean(name = "mbWorkOrderController")
#SessionScoped
public class WorkOrderController {
// more attributes...
private WorkOrder workOrderCurrent;
// more code here...
public WorkOrder getWorkOrderCurrent() {
return workOrderCurrent;
}
public void setWorkOrderCurrent(WorkOrder workOrderCurrent) {
this.workOrderCurrent = workOrderCurrent;
}
}
It holds a parameter workOrderCurrent of the custom type WorkOrder. The class WorkOrder has an attribute applicant of type String.
At the moment I am using a placeholder inside my inputtext to show the user, what he needs to type inside an inputText.
<p:inputText id="applicant"
value="#{mbWorkOrderController.workOrderCurrent.applicant}"
required="true" maxlength="6"
placeholder="#{mbUserController.userLoggedIn.username}" />
What I want to do, is to automatically pass the value of mbUserController.userLoggedIn.username to mbWorkOrderController.workOrderCurrent.applicant and remove the inputText for applicant completely from my form.
I tried to use c:set:
<c:set value="#{mbUserController.userLoggedIn.username}" target="#{mbWorkOrderController}" property="workOrderCurrent.applicant" />
But unfortunatelly I get a javax.servlet.ServletException with the message:
The class 'WorkOrderController' does not have the property 'workOrderCurrent.applicant'.
Does anybody have an advice?
The class 'WorkOrderController' does not have the property 'workOrderCurrent.applicant'.
Your <c:set> syntax is incorrect.
<c:set value="#{mbUserController.userLoggedIn.username}"
target="#{mbWorkOrderController}"
property="workOrderCurrent.applicant" />
You seem to be thinking that the part..
value="#{mbWorkOrderController.workOrderCurrent.applicant}"
..works under the covers as below:
WorkOrderCurrent workOrderCurrent = mbWorkOrderController.getWorkOrderCurrent();
workOrderCurrent.setApplicant(applicant);
mbWorkOrderController.setWorkOrderCurrent(workOrderCurrent);
This isn't true. It works under the covers as below:
mbWorkOrderController.getWorkOrderCurrent().setApplicant(applicant);
The correct <c:set> syntax is therefore as below:
<c:set value="#{mbUserController.userLoggedIn.username}"
target="#{mbWorkOrderController.workOrderCurrent}"
property="applicant" />
That said, all of this isn't the correct solution to the concrete problem you actually tried to solve. You should perform model prepopulating in the model itself. This can be achieved by using #ManagedProperty to reference another bean property and by using #PostConstruct to perform initialization based on it.
#ManagedBean(name = "mbWorkOrderController")
#SessionScoped
public class WorkOrderController {
#ManagedProperty("#{mbUserController.userLoggedIn}")
private User userLoggedIn;
#PostConstruct
public void init() {
workOrderCurrent.setApplicant(userLoggedIn.getUsername());
}
// ...
}
Perhaps you could explain the context a bit more, but here's another solution. If you're navigating from another page, you can pass some identifier of work WorkOrder in the URL, like this http://host:port/context/page.xhtml?workOrderId=1.
Then, you can set the identifier in the managed bean like this:
<h:html>
<f:viewParam name="workOrderId" value="#{mbWorkOrderController.id}"/>
</h:html>
You'll have to add a new property to your bean:
public class WorkOrderController {
private long id;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
// ...
}
And then, after the property has been set by JSF, you can find the work order in a lifecycle event:
<h:html>
<f:viewParam name="workOrderId" value="#{mbWorkOrderController.id}"/>
<f:event type="preRenderView" listener="#{mbWorkOrderController.findWorkOrder()}"/>
</h:html>
public class WorkOrderController {
private long id;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public void findWorkOrder() {
this.workOrderCurrent = null /* some way of finding the work order */
}
// ...
}
This strategy has the advantage of letting you have bookmarkable URLs.
I have used the HashMap method for binding a list of checkboxes to a Map<String, Boolean> with success. This is nice since it allows you to have a dynamic number of checkboxes.
I'm trying to extend that to a variable length list of selectManyMenu. Being that they are selectMany, I'd like to be able to bind to a Map<String, List<MyObject>>. I have a single example working where I can bind a single selectManyMenu to a List<MyObject> and everything works fine, but whey I put a dynamic number of selectManyMenus inside a ui:repeat and attempt to bind to the map, I end up with weird results. The values are stored correctly in the map, as verified by the debugger, and calling toString(), but the runtime thinks the map's values are of type Object and not List<MyObject> and throws ClassCastExceptions when I try to access the map's keys.
I'm guessing it has something to do with how JSF determines the runtime type of the target of your binding, and since I am binding to a value in a Map, it doesn't know to get the type from the value type parameter of the map. Is there any workaround to this, other than probably patching Mojarra?
In general, how can I have a page with a dynamic number of selectManyMenus? Without, of course using Primefaces' <p:solveThisProblemForMe> component. (In all seriousness, Primefaces is not an option here, due to factors outside of my control.)
The question UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T had some good information that I wasn't aware of, but I'm still having issues with this SSCE:
JSF:
<ui:define name="content">
<h:form>
<ui:repeat value="#{testBean.itemCategories}" var="category">
<h:selectManyMenu value="#{testBean.selectedItemMap[category]}">
<f:selectItems value="#{testBean.availableItems}" var="item" itemValue="#{item}" itemLabel="#{item.name}"></f:selectItems>
<f:converter binding="#{itemConverter}"></f:converter>
<f:validator validatorId="test.itemValidator"></f:validator>
</h:selectManyMenu>
</ui:repeat>
<h:commandButton value="Submit">
<f:ajax listener="#{testBean.submitSelections}" execute="#form"></f:ajax>
</h:commandButton>
</h:form>
</ui:define>
Converter:
#Named
public class ItemConverter implements Converter {
#Inject
ItemStore itemStore;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return itemStore.getById(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return Optional.of(value)
.filter(v -> Item.class.isInstance(v))
.map(v -> ((Item) v).getId())
.orElse(null);
}
}
Backing Bean:
#Data
#Slf4j
#Named
#ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
ItemStore itemStore;
List<Item> availableItems;
List<String> itemCategories;
Map<String, List<Item>> selectedItemMap = new HashMap<>();
public void initialize() {
log.debug("Initialized TestBean");
availableItems = itemStore.getAllItems();
itemCategories = new ArrayList<>();
itemCategories.add("First Category");
itemCategories.add("Second Category");
itemCategories.add("Third Category");
}
public void submitSelections(AjaxBehaviorEvent event) {
log.debug("Submitted Selections");
selectedItemMap.entrySet().forEach(entry -> {
String key = entry.getKey();
List<Item> items = entry.getValue();
log.debug("Key: {}", key);
items.forEach(item -> {
log.debug(" Value: {}", item);
});
});
}
}
ItemStore just contains a HashMap and delegate methods to access Items by their ID field.
Item:
#Data
#Builder
public class Item {
private String id;
private String name;
private String value;
}
ItemListValidator:
#FacesValidator("test.itemValidator")
public class ItemListValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (List.class.isInstance(value)) {
if (((List) value).size() < 1) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_FATAL, "You must select at least 1 Admin Area", "You must select at least 1 Admin Area"));
}
}
}
}
Error:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.util.List
Stacktrace snipped but occurs on this line:
List<Item> items = entry.getValue();
What am I missing here?
As hinted in the related question UISelectMany on a List<T> causes java.lang.ClassCastException: java.lang.String cannot be cast to T, generic type arguments are unavailable during runtime. In other words, EL doesn't know you have a Map<String, List<Item>>. All EL knows is that you have a Map, so unless you explicitly specify a converter for the selected values, and a collection type for the collection, JSF will default to String for selected values and an object array Object[] for the collection. Do note that the [ in [Ljava.lang.Object indicates an array.
Given that you want the collection type to be an instance of java.util.List, you need to specify the collectionType attribute with the FQN of the desired concrete implementation.
<h:selectManyMenu ... collectionType="java.util.ArrayList">
JSF will then make sure that the right collection type is being instantiated in order to fill the selected items and put in the model. Here's a related question where such a solution is being used but then for a different reason: org.hibernate.LazyInitializationException at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel.
Update: I should have tested the above theory. This doesn't work in Mojarra when the collection behind collectionType is in turn wrapped in another generic collection/map. Mojarra only checks the collectionType if the UISelectMany value itself already represents an instance of java.util.Collection. However, due to it being wrapped in a Map, its (raw) type becomes java.lang.Object and then Mojarra will skip the check for any collectionType.
MyFaces did a better job in this in its UISelectMany renderer, it works over there.
As far as I inspected Mojarra's source code, there's no way to work around this other way than replacing Map<String, List<Long>> by a List<Category> where Category is a custom object having String name and List<MyObject> selectedItems properties. True, this really kills the advantage of Map of having dynamic keys in EL, but it is what it is.
Here's a MCVE using Long as item type (just substitute it with your MyObject):
private List<Category> categories;
private List<Long> availableItems;
#PostConstruct
public void init() {
categories = Arrays.asList(new Category("one"), new Category("two"), new Category("three"));
availableItems = Arrays.asList(1L, 2L, 3L, 4L, 5L);
}
public void submit() {
categories.forEach(c -> {
System.out.println("Name: " + c.getName());
for (Long selectedItem : c.getSelectedItems()) {
System.out.println("Selected item: " + selectedItem);
}
});
// ...
}
public class Category {
private String name;
private List<Long> selectedItems;
public Category(String name) {
this.name = name;
}
// ...
}
<h:form>
<ui:repeat value="#{bean.categories}" var="category">
<h:selectManyMenu value="#{category.selectedItems}" converter="javax.faces.Long">
<f:selectItems value="#{bean.availableItems}" />
</h:selectManyMenu>
</ui:repeat>
<h:commandButton value="submit" action="#{bean.submit}">
<f:ajax execute="#form" />
</h:commandButton>
</h:form>
Do note that collectionType is unnecessary here. Only the converter is still necessary.
Unrelated to the concrete problem, I'd like to point out that selectedItemMap.entrySet().forEach(entry -> { String key ...; List<Item> items ...;}) can be simplified to selectedItemMap.forEach((key, items) -> {}) and that ItemListValidator is unnecessary if you just use required="true" on the input component.
I have a data table
<h:dataTable value="#{vendor.vh.currentVendorVO.vfms}" var="row">
In VendorVO
private VFM[] vfms;
public VFM[] getVfms() {
return vfms;
}
public void setVfms(VFM[] vfms) {
this.vfms = vfms;
}
In VFM
private String orderTypeId;
private String fulfillTypeId;
private int orderSeq;
private String lastUpdated;
private String lastUpdatedBy;
private boolean lastItem;
private String action = "none";
I would like to duplicate the <h:dataTable> row when a button is clicked.
How can I achieve this?
Use a dynamically expansible ArrayList instead of a fixed size array [].
private List<VFM> vfms; // +getter (setter is unnecessary)
Then, it's just a matter of letting the button invoke add() method on it with new VFM instance.
<h:commandButton value="Add" action="#{bean.addVfm}" />
public void addVfm() {
vfms.add(new VFM());
}
If you intend to have this button on every row which copies a new VFM instance, then just pass it along and add a copy constructor.
<h:commandButton value="Copy" action="#{bean.copyVfm(row)}" />
public void copyVfm(VFM vfm) {
vfms.add(new VFM(vfm));
}
public VFM(VFM vfm) {
orderTypeId = vfm.orderTypeId;
fulfillTypeId = vfm.fulfillTypeId;
orderSeq = vfm.orderSeq;
lastUpdated = vfm.lastUpdated;
lastUpdatedBy = vfm.lastUpdatedBy;
lastItem = vfm.lastItem;
action = vfm.action;
}
I'm using a <rich:datatable> to show the content of a List<Map<String, String>
In the code below, spotlightPtBindings is the List<Map<String, String> and spotlightbinding represents each Map<String, String>. In the first column, I'm showing one selectBooleanCheckBox for eah row. When a selectBooleanCheckBox is checked, I'd like to send the value of the Map<String, String> corresponding to the key "URI" as a parameter to the method: inserirBean.onSelectedCheckBox(uri), and that's why I put this value in a ui:param of name: uri. The problem here is that when I try to print the value uri received in inserirBean.onSelectedCheckBox(uri), I don't get the any output, as if it is empty. Below there's the rest of the code:
InsereDocumento.xhtml
<rich:dataTable value="#{inserirBean.spotlightPtBindings}" var="spotlightbinding">
<rich:column>
<f:facet name="header">*</f:facet>
<ui:param name="uri" value="#{spotlightbinding['URI']}"/>
<h:selectBooleanCheckbox value="#{selectionBean.selected}" />
<c:if test="#{selectionBean.selected}">
#{inserirBean.onSelectedCheckBox(uri)}"
</c:if>
</rich:column>
<c:forEach items="#{inserirBean.variableNamesPt}" var="vname">
<rich:column>
<f:facet name="header">#{vname}</f:facet>
#{spotlightbinding[vname]}
</rich:column>
</c:forEach>
</rich:dataTable> <br />
SelectionBean
package managedBeans;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
#ManagedBean
public class CheckBoxSelectionBean implements Serializable {
private transient boolean selected = false;
private static final long serialVersionUID = 1L;
public CheckBoxSelectionBean() {
// TODO Auto-generated constructor stub
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
InserirBean - I'm not showing here how the List<Map<String, String>> named spotlightPtBinding and how the List<String> variableNamesPt were populated, because it was a complex process, but I can tell you they are certainly populated, cause I can see their content on the screen.
#ManagedBean
public class InsereDocumentoBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> variableNamesPt = new ArrayList<String>();
private List<Map<String, String>> spotlightPtBindings = new ArrayList<Map<String, String>>();
public List<String> getVariableNamesPt() {
return variableNamesPt;
}
public List<Map<String, String>> getSpotlightPtBindings() {
return this.spotlightPtBindings;
}
public void onSelectedCheckBox(String uri) {
System.out.println("URI: " + uri);
}
}
What may the problem be? Thank you! I'm new to JSF and need your help!
In JSF rendering is a two-step process: there's view build time and view render time.
Although they're in the same file, some tags take effect at view build time, some at render time.
JSTL tags like <c:forEach>, <c:if> and all tag handlers (including <ui:param>, see here) are evaluated at view build time, they add content to the "final" xml tree that is then rendered by JSF.
JSF HTML tags and derivates like <rich:dataTable> are evaluated at view render time, so the datatable's var is evaluated later then the <ui:param> which causes spotlightbinding not to be bound when it's assigned to uri.
Instead, I suggest you assign an ajax listener to call the function:
<h:selectBooleanCheckbox value="#{selectionBean.selected}">
<f:ajax listener="#{inserirBean.onSelectedCheckBox(spotlightbinding['URI'])}" />
</h:selectBooleanCheckbox>
Note that the listener is called whenever the value changes.
Hi i need to dyamically set h:commandLink action as a string value from bean side. Here explains my problem with code
MenuObject.java
public class MenuObject {
private String menuName;
private String menuAction;
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public String getMenuAction() {
return menuAction;
}
public void setMenuAction(String menuAction) {
this.menuAction = menuAction;
}
}
MenuCreator.java
public class MenuCreator {
public List getMenu(){
List menuList = new ArrayList();
MenuObject menu1 = new MenuObject();
menu1.setMenuAction("accountController.beginSearch()");
menu1.setMenuName("Account");
menuList.add(menu1);
MenuObject menu2 = new MenuObject();
menu2.setMenuAction("companyController.beginSearch()");
menu2.setMenuName("Company");
menuList.add(menu1);
return menuList;
}
main.xhtml
<ui:repeat value="#{menuCreator.menu}" var="subMenu">
<li class="glyphicons cogwheels"><h:commandLink action="#{subMenu.menuAction}"><i></i><span><h:outputText value="#{subMenu.menuName}"/></span></h:commandLink></li>
</ui:repeat>
Here what i need is i need to dynamically change commandlink action value with respect to bean string value (here it was menuAction). But in this situation i got following exception
javax.el.MethodNotFoundException: /WEB-INF/layouts/main.xhtml #137,85 action="#{menuCreator.menu}": Method not found: com.util.MenuObject#30c96021.menuAction()
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:109)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:101)
at net.bull.javamelody.JsfActionListener.processAction(JsfActionListener.java:65)
You are trying to use the EL to return a value expression to be used as a method expression in a single expression. The JEE7 tutorial states:
9.3 Value and Method Expressions
The EL defines two kinds of expressions: value expressions and method expressions.
Value expressions can either yield a value or set a value. Method expressions reference
methods that can be invoked and can return a value.
You can achive this behaviour using javascript or use a library that offers you a dynamic menu component, like primefaces.
Maybe you can try something like Command Pattern. This is only an idea, I did not tested it.
In the xhtml:
<ui:repeat value="#{menuCreator.menu}" var="subMenu">
<li class="glyphicons cogwheels">
<h:commandLink action="#{invoker.callAction}" value="#{subMenu.menuName}">
<f:setPropertyActionListener target="#{invoker.action}" value="#{subMenu.action}" />
</h:commandLink>
</li>
</ui:repeat>
The command pattern:
/* The Command interface */
public interface Command {
String execute();
}
The menu item:
public class MenuObject {
private String menuName;
private Command action;
// Getters and setters...
}
The invoker:
#Named("invoker")
public class Invoker {
private Command action;
public String callAction(){
return action.execute();
}
}