How to use Primefaces Dialog with #ViewScoped setting Parameters? - jsf

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}" />

Related

Automatically set value of a managed bean variable with JSF

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.

Set property of another bean after action method is invoked

Mojarra 2.2
I have two beans`.
public class MyBean1{
private String myProperty1;
//GET, SET, CTOR
public void doAction(){
//assign something to myProperty1
}
}
public class MyBean2{
private String myProperty2;
//GET, SET, CTOR
}
Now, I need to assign the value of the property MyBean1::myProperty1 to MyBean2::myProperty2 after the doAction() method invocation by clicking a button:
<h:commandButton action="#{myBean1.doAction}">
<f:setPropertyActionListener target="#{myBean2.myProperty2}"
value="#{myBean1.myProperty1}" />
</h:commandButton>
But it doesn't work. And I figured out that it doesn't work due to the following reason:
Clicking a button causes ActionEvent to be broadcasted. It performs by this method (javax.faces.component.UICommand):
public void broadcast(FacesEvent event) throws AbortProcessingException {
super.broadcast(event); //1 <-------------------- HERE
if (event instanceof ActionEvent) {
FacesContext context = getFacesContext();
MethodBinding mb = getActionListener();
if (mb != null) {
mb.invoke(context, new Object[] { event });
}
ActionListener listener =
context.getApplication().getActionListener();
if (listener != null) {
listener.processAction((ActionEvent) event);
}
}
}
I've noticed that the ActionEvent broadcasting by clickng the button is being handled by two listeners and one that comes from super.broadcast(event) invokation at //1 is com.sun.faces.facelets.tag.jsf.core.SetPropertyActionListenerHandler.SetPropertyListener.
The handling is performed before invokation of the action method.
Of course, I can embed the action method invokation into the MyBean1:getMyProperty1() getter, so the myProperty1 field will be properly intialized, but it seems quite wierd to me. What is the right way to achieve that?

How do I use a MethodExpression with parameters for a custom JSF component? [duplicate]

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;
}
}

How to use Streamed Content with p:fileDownload to Download Non class path File

I'm using Primefaces
p:fileDownload
to download a file which is not in class path.
So I'm passing FileInputStream as parameter to DefaultStreamedContent.
Every thing works fine when my bean is kept at #SessionScoped...,
But
java.io.NotSerializableException: java.io.FileInputStream
is thrown when I keep my bean in #Viewscoped.
My Code:
DownloadBean.java
#ManagedBean
#ViewScoped
public class DownloadBean implements Serializable {
private StreamedContent dFile;
public StreamedContent getdFile() {
return dFile;
}
public void setdFile(StreamedContent dFile) {
this.dFile = dFile;
}
/**
* This Method will be called when download link is clicked
*/
public void downloadAction()
{
File tempFile = new File("C:/temp.txt");
try {
dFile = new DefaultStreamedContent(new FileInputStream(tempFile), new MimetypesFileTypeMap().getContentType(tempFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
index.xhtml
<h:form>
<h:commandLink action="#{downloadBean.downloadAction}">
Download
<p:fileDownload value="#{downloadBean.dFile}"/>
</h:commandLink>
</h:form>
Isn't there any method to make it work?
The NotSerializableException is thrown because the view scope is represented by the JSF view state which can in turn be serialized to HTTP session in case of server side state saving or a HTML hidden input field in case of client side state saving. The FileInputStream can in no way be represented in a serialized form.
If you absolutely need to keep the bean view scoped, then you should not be declaring StreamedContent as an instance variable, but instead recreate it in the getter method. True, doing business logic in a getter method is usually frowned upon, but the StreamedContent is a rather special case. In the action method, you should then only prepare serializable variables which are later to be used during DefaultStreamedContent construction.
#ManagedBean
#ViewScoped
public class DownloadBean implements Serializable {
private String path;
private String contentType;
public void downloadAction() {
path = "C:/temp.txt";
contentType = FacesContext.getCurrentInstance().getExternalContext().getMimeType(path);
}
public StreamedContent getdFile() throws IOException {
return new DefaultStreamedContent(new FileInputStream(path), contentType);
}
}
(note that I also fixed your way to get the content type; you have this way much more freedom to configure mime types via <mime-mapping> entries in web.xml)
The <p:graphicImage> has by the way exactly the same problem with StreamedContent. See also among others Display dynamic image from database with p:graphicImage and StreamedContent.
#BalusC, for p:fileDownload, is there a way to offload the creation of the StreamedContent to another object which could then be called directly from JSF? Similar to the way you offload p:graphicImage here. If so what would be the scope of this special object? I'm guessing RequestScoped since there would be no connection between initDownload and getDownload. ApplicationScoped would not be able to keep track of all downloads within a single session, right? I also wonder if creating a new Apache FOP object in every Request is too expensive?
Here's an example:
jsf:
<h:commandButton value="print/download" action="#{streamhelper.initDownload()}">
<p:fileDownload value="#{streamhelper.download}"/>
<f:param name="html" value="#{bean.html}" />
<f:param name="idNum" value="#{bean.idNum}" />
</h:commandButton>
special object:
#Named("streamhelper") #RequestScoped #Getter #Setter #Slf4j
public class StreamedContentHelper
{
#PostConstruct #SneakyThrows({NamingException.class})
public void init(){
fop = util.getLocator().getObject(util.getLocator().prependPortableName(FOPEngineImpl.class.getSimpleName()));
}
public void initDownload() throws Exception
{
FacesContext context = FacesContext.getCurrentInstance();
log.trace("context PhaseID: {}", context.getCurrentPhaseId());
String html = context.getExternalContext().getRequestParameterMap().get("html");
String idNum = context.getExternalContext().getRequestParameterMap().get("idNum");
byte[] attachBytes = fop.getPDFBytes(html);
InputStream stream = new ByteArrayInputStream(attachBytes);
stream.mark(0); //remember to this position!
String filename = String.format("%s-download.pdf", loadNum);
download = new DefaultStreamedContent(stream, "application/pdf", filename);
}
private StreamedContent download;
private FOPEngineLocal fop;
private #Inject Util util;
}

JSF - Updating Model Values in Controller Bean

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);
}

Resources