This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 5 years ago.
I wrote this question:
https://stackoverflow.com/questions/8589315/jsf2-dynamic-template
but BalusC and casperOne told that i wrote it bad so I try to explain better my problem.
As I wrote, I have my project in this structure:
in web root 3 xhtml pages: index, include and welcome;
all others xhtml pages in a subfolder into WEB-INF called jsf.
I suppose that it is a good thing, but I create all pages using the netbeans' wizard "New JSF Pages From Entity Classes..." and for using this structure with this wizard, I can't link directly the xhtml pages saved into jsf forlder, as created by wizard, and I created the include.xhtml and modify all methods for redirecting to this page, as I will explain later, the include.xhtml contains only this code:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
</h:head>
<h:body>
Utente connesso:<h:outputText value="#{userBean.cognome}"/>
<h:outputText value="#{userBean.nome}"/>
<br/&gr;
<ui:include src="#{logicBean.pageIncluded}"/>
</h:body>
</html>
As I said, I modified all methods to call action method in LogicBean that contains the setPageIncluded and return "include.xhtml" something like this:
PageController.java:
public void prepareList() {
recreateModel();
LogicBean l = (LogicBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("logicBean");
l.action(url+"List.xhtml");
}
LogicBean.java:
public String action(String value) {
setPageIncluded(value);
return "include";
}
Now this code works quite well and I can navigate from to all pages.
There is only a problem, the bean LogicBean is stored into the session!
This means that if I tried to open a new windows for navigate different part of the project in the same time I can't do that because LogicBean can contains only a single value of pageInclude!
I tried to use the ViewScope but or I don't understand how it works, and in this case it isn't useful, or I wrong something and it doesn't work properly!
Some one can help me?
I found this question that seems could help me:
dynamic ui:include with el-expression?
but I don't know if it could help and how modify the c:forEach and action method for using it for my situation!
I hope that this time I explain better my problem and, if it so, I thank you for help!
You use a JEE6 certified server, so you can use CDI for bean management. There is a CDI extension library called MyFaces CODI that has a Window scope bundle in it and you can use it to scope your beans instead of using session scope. This will solve your problem with the bean scoping.
Home page - http://myfaces.apache.org/extensions/cdi/
Related
I am quite new in developing website with JSF and PrimeFaces, I spent a lots of hours to research about my problem before post here. Thanks to everyone who spent time to read my question.
Well, I'm using this in my menu template page:
<h:link value="Manage Examination" outcome="/backend/examination/index" />
...
<h:link value="List Examinations..." outcome="/WEB-INF/include/backend/examination/List.xhtml" />
<h:link value="Add Examination..." outcome="/WEB-INF/include/backend/examination/Create.xhtml" />
My WEB-INF folder has this structure like below:
WEB-INF
\--include
\\-----backend
\\------'entity name'
\\\-------'create,read,update,delete.xhtml'
Outside of WEB-INF is my root web page folder, I have backend folder here, and its structure:
webpages
\--WEB-INF
\--backend
\\---'entity name'
\\\---index.xhtml
Inside each html I put these code:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/include/templates/backend.xhtml">
<ui:define name="title">
<h:outputText value="#{appBundle.ExaminationTitle}"/>
</ui:define>
<ui:define name="body">
<ui:include src="/WEB-INF/include/backend/examination/List.xhtml"/>
</ui:define>
</ui:composition>
And my question is:
I didn't configure anything in faces-config.xml so could JSF know if I click to the link ?
How could I keep the url in address bar always be /backend/examination/index even I click to the 'List Examination' or 'Add Examination' link with the outcome to /WEB-INF/include/backend/examination/* ?
NNToan
You are misusing <h:link>.
The value of the 'outcome' attribute should be bound to a path whose root is the main faces context path. The JSF framework will render your h:link with an <a/> tag, so keeping the same address is out of question.
<h:link value="Page 1" outcome="page1.xhtml" />
will be in fact translate into:
Page 1
If you want to perform an action without user redirection you should use a commandLink or a commandButton, returning a String (also take care not to include the faces-redirect parameter in that string).
For instance:
<h:commandLink value="Click here" action="#{YourBean.myAction}"/>
In your backing bean:
public String myAction()
{
// do your stuff here
return ""; // in general return the view you want to be redirected on, "" means "here"
}
1.You don't need to. h:link is rendered as an HTML element. So clicking the link issues simple HTTP GET request to the specified URL, no JSF postback, no action listener and no dynamic navigation, and so nothing to do with the faces-config.xml in this case.
2.You can't. Since this is an HTTP GET request, browser will no way display the target URL.
To preserve the URL you have to make a JSF postback and make dynamic navigation with an action listener method that returns an outcome that is used by the navigation handler.
JSF always makes a postback to the same page and so URL is preserved although a new view is rendered.
<h:form>
<!--When you click the button you navigate to the display Examinations view -->
<!--but URL in the browser address bar does not change-->
<h:commandButton value="Navigate" action="#{bean.displayExaminationsList()}"/>
</h:form>
#ManagedBean
public class Bean {
public String displayExaminationsList() {
//The view that maps to this viewID shall be defined in the faces-config.xml
return "examinationsListViewID";
}
}
I have backing bean like this:
#ManagedBean
#SessionScoped
public class TestBean {
private String testString;
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
}
And my xhtml page pretty simple too:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<h:head></h:head>
<h:body>
<h:form>
<h:inputText value="#{testBean.testString}"/>
<h:commandButton action="#{testController.testAction}"/>
</h:form>
</h:body>
</html>
Everything I want - to render my h:inputText element without value (empty).
I'm new to JSF, so, could you help me?
With best regards!
UPD!
It's simplified code, I'm using testString in other places and testString have value, which I want to hide! And I want to keep this value.
Provided that it's really a request/view scoped bean, you're likely victim of browser's builtin autocomplete/autofill feature. You can turn it off by adding autocomplete="off" to the input component in question.
<h:inputText ... autocomplete="off" />
Note again that it's not JSF who has filled the inputs, but the webbrowser itself. Clear the browser cache and you'll see that the browser won't do it anymore. Depending on browser make/version you can also reconfigure it to autocomplete a bit less eagerly.
Update: as per your question update, your bean turns out to be session scoped. This is not the normal scope for request/view based forms. A session scoped bean instance is shared across all browser windows/tabs (read: all requests/views) in the same HTTP session. You usually store only the logged-in user and its preferences (language, etc) in the session. You will only get a brand new instance when you shutdown and restart the entire browser, or use a different browser/machine.
Change it to be request or view scoped. In this particular simple example, the request scope should suffice:
#ManagedBean
#RequestScoped
See also:
How to choose the right bean scope?
Update 2 based on the comment,
Oh, you right, it's better for me to use #RequestScoped. But it doesn't resolve my problem - I want to keep this value, but I don;t want to show it in textInput. This value is important in context of request-response cycle.
the concrete functional requirement is now much more clear (in future questions, please pay attention to that while preparing the question, I had no idea that you was initially asking it like that). In that case, use a view scoped bean with 2 properties like this:
#ManagedBean
#ViewScoped
public class TestBean {
private String testString;
private String savedTestString;
public void testAction() {
savedTestString = testString;
testString = null;
}
// ...
}
You can alternatively also store it in the database or a property of an injected managed bean which is in turn actually in the session scope, for example.
You should bind the input text to some other field in your backing bean. And if you want to use that field for yourtestString, copy the entered value to testString in the testAction method.
<h:form>
<h:inputText value="#{testBean.copyTestString}"/>
<h:commandButton action="#{testController.testAction}"/>
</h:form>
public String testAction()
{
testString = copyTestString;
return "destinationPage";
}
Some Browsers ignore autocomplete - it can help to put autocomplete in form tag:
<h:form autocomplete="off">
I rode that is recommended to use CDI beans as backing beans instead of JSF managed beans.
So i decided to create a little example, to understand how it works, for a #RequestScopedBean:
-instead of using #ManagedBean("beanName") ,i use #Named("beanName")
-instead of using javax.faces.bean.RequestScopped i use javax.enterprise.context.RequestScoped;
The demo program is very simple, i have a field and a submit button, when the user inputs something and the page is refreshed, the inputed value is not displayed anymore(It last while the request lasts right?). I think i did everything ok, but i get an exception that says:
WARNING: StandardWrapperValve[Faces
Servlet]: PWC1406: Servlet.service()
for servlet Faces Servlet threw
exception
javax.el.PropertyNotFoundException:
/index.xhtml #19,47
value="#{cdiBean.passedValue}": Target
Unreachable, identifier 'cdiBean'
resolved to null
This is how my program looks like:
index.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>RequestScope demo CDI(Component Dependency Injection)</title>
</h:head>
<h:body>
<h:form>
<h3>RequestScope demo CDI(Component Dependency Injection)</h3>
<h:inputText value="#{cdiBean.passedValue}"/>
<br/>
<h:commandButton value="submit" action="index"/>
</h:form>
</h:body>
</html>
DemoBB.java
package backingbeans;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named("cdiBean")//The Named anotation indicates that this is a CDI bean
#RequestScoped//If we use CDI beans the #RequestScoped annotation must come from: javax.enterprise.context.RequestScoped;
public class DemoBB {
//This value will be saved on the session only until the server responds to the request
private String passedValue;
public String getPassedValue() {
return passedValue;
}
public void setPassedValue(String passedValue) {
this.passedValue = passedValue;
}
}
-Where is my mistake?
-What is the advantage of using this approach? I still don't understand that.
Do you have an empty beans.xml along with your web.xml? I think it is mandatory to be there.
Read section 15.6 here. Quote from it:
CDI doesn't define any special
deployment archive. You can package
beans in JARs, EJB-JARs or WARs—any
deployment location in the application
classpath. However, the archive must
be a "bean archive". That means each
archive that contains beans must
include a file named beans.xml in the
META-INF directory of the classpath or
WEB-INF directory of the web root (for
WAR archives). The file may be empty.
Beans deployed in archives that do not
have a beans.xml file will not be
available for use in the application.
In an embeddable EJB container, beans
may be deployed in any location in
which EJBs may be deployed. Again,
each location must contain a beans.xml
file.
I've got a .jsp file that is working fine. It's maybe a bit special in that it is calling a factory (ArticlesFactory) that returns a singleton (but that is a detail) of class Articles (it does so by automatically fetching shared Google Docs that are transformed to html and then stored into ".../text/en" but that is a detail too).
The following is working fine: it does exactly what I need, it fetches the articles automatically and I can access my Articles instance fine.
<%# page pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
<%# page import="com.domain.projectname.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en">
<head></head>
<body>
<% Articles articles = ArticlesFactory.create( getServletContext().getRealPath( "text/en" )); %>
We have <%= articles.getNbItems()%>
</body>
</html>
However, I must transform it to some notation I don't know nor understand, I'm not even sure what the name for that is and obviously I've got some issue.
I don't know if it's a namespace issue or if there's a problem with the ArticlesFactory factory's static factory method creating the Articles singleton:
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="urn:jsptld:http://java.sun.com/jsp/jstl/core">
<jsp:directive.page import="com.domain.project.ArticlesFactory"/>
<jsp:directive.page contentType="text/html; charset=UTF-8" />
We have ${variable.nbItems} <!-- What to put here !? -->
</jsp:root>
I tried many things and couldn't figure it out.
Basically I need to:
- call the static create method from the ArticlesFactory class
- by passing it the result of getServletContext().getRealPath( "text/en" ))
(which should give back an Articles instance)
then I want to put the result of getNbItems() in a variable that I want to display
Note that I don't want to have to call getServletContext from any servlet/dispatcher: I want to do it just like in the first working example (ie directly from inside the .jsp).
You're basically looking for "JSP in XML syntax". Most is already explained in this (old) tutorial. You yet have to replace <% %> by <jsp:scriptlet> and <%= %> by <jsp:expression>.
The xmlns:c namespace is by the way unnecessary here, unless you'd like to use any of the JSTL core tags.
The Expression Language (those ${} things) which is explained in this (also old) tutorial is by the way a separate subject. It only acts on objects in the page, request, session or application scope. In scriptlets however, variables are only defined in local scopes (methodlocal actually), those aren't available in EL. You would need to do the following in the scriptlet to make it available in EL:
pageContext.setAttribute("articles", articles); // Put in page scope (recommended).
request.setAttribute("articles", articles); // Or in request scope. Also accessible by any include files.
session.setAttribute("articles", articles); // Or in session scope. Accessible by all requests in same session.
application.setAttribute("articles", articles); // Or in application scope. Accessible by all sessions.
This way it's available by ${articles} in EL.
Does Facelets have any features for neater or more readable internationalised user interface text labels that what you can otherwise do using JSF?
For example, with plain JSF, using h:outputFormat is a very verbose way to interpolate variables in messages.
Clarification: I know that I can add a message file entry that looks like:
label.widget.count = You have a total of {0} widgets.
and display this (if I'm using Seam) with:
<h:outputFormat value="#{messages['label.widget.count']}">
<f:param value="#{widgetCount}"/>
</h:outputFormat>
but that's a lot of clutter to output one sentence - just the sort of thing that gives JSF a bad name.
Since you're using Seam, you can use EL in the messages file.
Property:
label.widget.count = You have a total of #{widgetCount} widgets.
XHTML:
<h:outputFormat value="#{messages['label.widget.count']}" />
This still uses outputFormat, but is less verbose.
You could create your own faces tag library to make it less verbose, something like:
<ph:i18n key="label.widget.count" p0="#{widgetCount}"/>
Then create the taglib in your view dir: /components/ph.taglib.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet">
<namespace>http://peterhilton.com/core</namespace>
<tag>
<tag-name>i18n</tag-name>
<source>i18n.xhtml</source>
</tag>
</facelet-taglib>
create /components/i18n.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:outputFormat value="#{messages[key]}">
<!-- crude but it works -->
<f:param value="#{p0}" />
<f:param value="#{p1}" />
<f:param value="#{p2}" />
<f:param value="#{p3}" />
</h:outputFormat>
</ui:composition>
You can probably find an elegant way of passing the arguments with a little research.
Now register your new taglib in web.xml
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>
/components/ph.taglib.xml
</param-value>
</context-param>
Just add xmlns:ph="http://peterhilton.com/core" to your views and you're all set!
I've never come across another way of doing it other than outputFormat. It is unfortunately quite verbose.
The only other thing I can suggest is creating the message in a backing bean and then outputting that rather than messageFormat.
In my case I have Spring's MessageSource integrated with JSF (using MessageSourcePropertyResolver). Then, it's fairly easy in your backing beans to get parameterised messages - you just need to know which Locale your user is in (again, I've got the Locale bound to a backing bean property so it's accessible via JSF or Java).
I think parameters - particular in messages - are one thing JSF could really do better!
I have been thinking about this more, and it occurs to me that I could probably write my own JSTL function that takes a message key and a variable number of parameters:
<h:outputText value="#{my:message('label.widget.count', widgetCount)}"/>
and if my message function HTML-encodes the result before output, I wouldn't even need to use the h:outputText
#{my:message('label.widget.count', widgetCount)}
You can use the Seam Interpolator:
<h:outputText value="#{interpolator.interpolate(messages['label.widget.count'], widgetCount)}"/>
It has #BypassInterceptors on it so the performance should be ok.
You can use the Bean directly if you interpolate the messages.
label.widget.count = You have a total of #{widgetCount} widgets.
label.welcome.message = Welcome to #{request.contextPath}!
label.welcome.url = Your path is ${pageContext.servletContext}.
${messages['label.widget.count']} is enougth.
This one works great using Spring:
package foo;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ExpressionFactory;
import javax.el.ResourceBundleELResolver;
import javax.faces.context.FacesContext;
import org.springframework.web.jsf.el.SpringBeanFacesELResolver;
public class ELResolver extends SpringBeanFacesELResolver {
private static final ExpressionFactory FACTORY = FacesContext
.getCurrentInstance().getApplication().getExpressionFactory();
private static final ResourceBundleELResolver RESOLVER = new ResourceBundleELResolver();
#Override
public Object getValue(ELContext elContext, Object base, Object property)
throws ELException {
Object result = super.getValue(elContext, base, property);
if (result == null) {
result = RESOLVER.getValue(elContext, base, property);
if (result instanceof String) {
String el = (String) result;
if (el.contains("${") | el.contains("#{")) {
result = FACTORY.createValueExpression(elContext, el,
String.class).getValue(elContext);
}
}
}
return result;
}
}
And...
You need to change the EL-Resolver in faces-config.xml from org.springframework.web.jsf.el.SpringBeanFacesELResolver to
Regards
<el-resolver>foo.ELResolver</el-resolver>
Use ResourceBundle and property files.