I have a Controller bean (SearchController) that has two managed bean as managed properties (SearchCriteria, SearchResults; both of which are session scoped).
When the user hits the find button, the action method that is executed is in SearchController.
The SearchCreteria managed bean has a method called search(). This method returns a new SearchResults object. In the controller bean, I am setting the searchResults managed property to be this new SearchResults object. The searchResults object contains what I expect during that request, but the object does not persist in the managed bean.
I understand that I am changing what object that searchResults is referencing, but what I don't understand is why JSF isn't updating the model to use the new object. Any ideas what I'm missing or don't understand? I am using JSF 1.1 on WebSphere 6.1.
If I put the search method in the SearchResults managed bean, it works.
The line in SearchController.find() that is commented out is the one that presently works.
public class SearchController {
SearchCriteria searchCriteria;
SearchResults searchResults;
ResultsBacking resultsBacking;
public String find()
{
setSearchResults(searchCriteria.search());
// searchResults.findSearchResults(searchCriteria);
if (!searchResults.resultsFound())
{
return "noresults";
}
return "success";
}
public class SearchCriteria {
public SearchResults search()
{
SearchDAO sdao = new SearchDAO();
ArrayList<Group> list = (ArrayList<Group>)sdao.findGroups(this);
SearchResults searchResults = new SearchResults();
searchResults.setSearchResults(list);
return searchResults;
}
}
public class SearchResults {
List<Group> searchResults;
public void findSearchResults(SearchCriteria criteria)
{
SearchDAO sdao = new SearchDAO();
this.setSearchResults(sdao.findGroups(criteria));
}
}
In a nut, you've something like this:
#ManagedBean
public class SearchController {
#ManagedProperty(value="#{searchCriteria}")
private SearchCriteria searchCriteria;
#ManagedProperty(value="#{searchResults}")
private SearchResults searchResults;
public void find() {
searchResults = searchCriteria.search();
}
}
And the #{searchResults} in the view doesn't contain the desired results?
This sounds like as if you're accessing the search results by #{searchResults.someData} instead of #{searchController.searchResults.someData} and you're expecting that overriding the SearchResults property inside the SearchController will replace the current session scoped managed bean.
This is wrong.
You need to solve it by either using #{searchController.searchResults.someData} instead
<h:outputText value="#{searchController.searchResults.someData}" />
Or by overriding (setting) the properties of SearchResults instead of overriding the whole managed property of SearchController:
public void find() {
searchResults.setSomeData(searchCriteria.search().getSomeData());
}
Or by manually replacing the bean in session (not recommended).
public void find() {
searchResults = searchCriteria.search();
FacesContext.getCurrentInstance().getExternalContext()
.getSessionMap().put("searchResults", searchResults);
}
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'm trying to develop a custom component that will need to call a method from the backingbean to get some data from the bb (this will be called in the decode phase after a certain Ajax call) with one parameter (it will come in the ajax call).
The problem I'm having is that I define the attribute as a MethodExpression (in the taglibrary and the component), I get the Ajax post, decode the parameter and when I try to get the Method binding from the component I get the following error:
javax.el.PropertyNotFoundException: /easyFaces.xhtml #19,151
dataSource="#{theBean.loadDataFromSource}": The class
'ar.com.easytech.faces.test.homeBean' does not have the property
'loadDataFromBean'.
Here is the relevant code.. (and please let me know if this is not the correct way to do this..)
taglib:
<attribute>
<display-name>Data Source</display-name>
<name>dataSource</name>
<required>true</required>
<type>javax.el.MethodExpression</type>
<method-signature>java.util.List theDataSource(java.lang.String)</method-signature>
</attribute>
Component definition:
public class Autocomplete extends HtmlInputText implements ClientBehaviorHolder
...
public MethodExpression getDataSource() {
return (MethodExpression) getStateHelper().eval(PropertyKeys.dataSource);
}
public void setDataSource(MethodExpression dataSource) {
getStateHelper().put(PropertyKeys.dataSource, dataSource);
}
and finally the rendered method that generates the error:
private List<Object> getData(FacesContext context, Autocomplete autocomplete, String data) {
Object dataObject = null;
MethodExpression dataSource = autocomplete.getDataSource();
if (dataSource != null) {
try {
dataObject = dataSource.invoke(context.getELContext(), new Object[] {data});
return convertToList(dataObject);
} catch (MethodNotFoundException e) {
logger.log(Level.INFO,"Method not found: {0}", dataSource.getExpressionString() );
}
}
return null;
}
Here is the method from the BB
public List<String> autcompleteFromSource(String param) {
List<String> tmpData = new ArrayList<String>();
tmpData.add("XXA_TABLE_A");
tmpData.add("XXA_TABLE_B");
tmpData.add("XXA_TABLE_C");
return tmpData;
}
And the .xhtml with the component
<et:autocomplete id="autoc" minLength="3" delay="500" value="#{easyfacesBean.selectedValue}" dataSource="#{easyfacesBean.autcompleteFromSource}" />
The thing is if I define a method getAutocompleteFromSource() it recognised the method and the error changes to can't convert list to MethodExpression, so evidently it is simply interpreting the autocompleteFromSource as a simple property and not a method definition, is this even the correct way to call method from BB? (giving that it's not an actual action nor validation )
I found the solution for this, as it turns out you also need to define a "Handler"to define the Method Signature, so I created the handler and added to the taglib and everything started to work fine..just for reference.. here is the handler..
Regards
public class AutocompleteHandler extends ComponentHandler {
public AutocompleteHandler(ComponentConfig config) {
super(config);
}
protected MetaRuleset createMetaRuleset(Class type) {
MetaRuleset metaRuleset = super.createMetaRuleset(type);
metaRuleset.addRule(new MethodRule("dataSource", List.class, new Class[] { String.class }));
return metaRuleset;
}
}
I'm facing again problem with Dialog.
What I intend to do is a common dialog that will be used for the whole appication, It has its own managed bean that is inherited by other MBs that need to use it, then some parameters are set by example super.setParam(...) to set some data to be displayed.The problem is that when the dialog is being loaded a getter method is called to retrieve the parameters set and it is no longer there, its just null.
I believe that due to the MB being #ViewScoped the container is creating a new instance when the dialog is beying loaded, but the setter method was called before it, so the new instance return to the default values. Using a #SessionScoped would solve the problem but it is not a good choice.
As a work around I tried to set the parameter in the request and then read it in the getter method but it is no longer there either.
Is there some way to get it working?
//here I put the parameter
public void setParam(MyObject myObject) {
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("params", myObject);
public MyObject getMyObject() {
// now the parameter is no longer there...
Object objet = FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("params");
.... after doing some things
return anotherObject;
}
}
EDIT<<<<
All starts with this button on a "client.xhtml"
<p:commandButton value="Call Dialog" ajax="true"
actionListener="#{subDialogMB.startProcess}"
>
</p:commandButton>
#javax.faces.bean.ManagedBean
#javax.faces.bean.ViewScoped
public class SubDialogMB extends SuperDialogMBean() {
public void starProcess() {
try {
MyObject myObject = service.CreateMyObject....
super.setMyObject();
super.shpwDialog();
}
}
#javax.faces.bean.ManagedBean
public class SuperDialogMBean() {
public void setMyObject(MyObject myObject) {
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("params", myObject);
}
public MyObject getMyObject(){
public MyObject getMyObject() {
// now the parameter is no longer there...
Object objet = FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("params");
.... after doing some things
return anotherObject;
}
}
public void showDialog() {
Map<String,Object> options = new HashMap<String, Object>();
options.put("modal", true);
options.put("draggable", false);
options.put("resizable", false);
options.put("contentHeight", 900);
options.put("contentWidth", 1100);
RequestContext.getCurrentInstance().openDialog("myDialog", options, null);
}
}
and finally in the Dialog xhtml
<p:outputText value="#{superDialogMBean.someValue}" />
I want to access the property of a #SessionScoped bean in another bean using #ManagedProperty. In short, I want to access the name property of firstBean in secondBean.
#ManagedBean
#SessionScoped
public class FirstBean implements Serializable{
private String name;
//...other attributes
//...constructor
public String getSelectedModel() {
return selectedModel;
}
public void setSelectedModel(String selectedModel) {
this.selectedModel = selectedModel;
}
//other getters&setters
}
And second bean:
#ManagedBean
#SessionScoped
public class SecondBean implements Serializable{
#ManagedProperty(value="#{firstBean}")
private FirstBean firstBean
public SecondBean() {
System.out.println(firstBean.getName());
}
public IndexBean getFirstBean() {
return firstBean;
}
public void setFirstBean(FirstBean firstBean) {
this.firstBean = firstBean;
}
When I run this, I always get NullPointerException on System.out.println(firstBean.getName()); in the constructor of second bean, which seems to mean that I need to create a new instance of firstBean.
But strangely, when I commented out this line, I can do something like this with no errors, which means that firstBean is actually a property of secondBean.
<h:outputText value="#{secondBean.firstBean.name}" />
What's the problem here?
It's not possible to access an injected dependency in the constructor. You're basically expecting that Java is able to do something like this:
SecondBean secondBean; // Declare.
secondBean.firstBean = new FirstBean(); // Inject.
secondBean = new SecondBean(); // Construct.
It's absolutely not possible to set an instance variable if the instance is not constructed yet. Instead, it works as follows:
SecondBean secondBean; // Declare.
secondBean = new SecondBean(); // Construct.
secondBean.firstBean = new FirstBean(); // Inject.
Then, in order to perform business actions based on injected dependencies, use a method annotated with #PostConstruct. It will be invoked by the dependency injection manager directly after construction and dependency injection.
So, just replace
public SecondBean() {
System.out.println(firstBean.getName());
}
by
#PostConstruct
public void init() { // Note: method name is fully to your choice.
System.out.println(firstBean.getName());
}
Ok, I think I've seen all the matches about this in StackOverflow and other Internet sites. My code is as follows:
Class:
public enum pruebaEnum{PRUEBA1, PRUEBA2, PRUEBA3};
private pruebaEnum prueba;
private pruebaEnum[] pruebaList;
public pruebaEnum getPrueba() {
return prueba;
}
public void setPrueba(pruebaEnum prueba) {
this.prueba = prueba;
}
public pruebaEnum[] getPruebaList() {
return pruebaEnum.values();
}
public void setPruebaList(pruebaEnum[] pruebaList) {
this.pruebaList = pruebaList;
}
JSF code:
<t:selectOneMenu id="categorization" value="#{BookManual.prueba}">
<t:selectItems Value="#{BookManual.pruebaList}"/>
</t:selectOneMenu>
The fact is I only get an empty dropbox. I don't know what I am doing wrong....
Attribute names are case sensitive. You used Value, but it's value.
By the way, you don't need a setter for <f:selectItems>. Get rid of it to save dead code and unnecessary future confusions because it's never invoked.