Currently i'm working on some complex web front-end and implement it using:
JSF 1.2
Facelets 1.1.15
RichFaces 3.3.3.Final
I have created a custom JSF component which enables validation of inputText fields using pure JavaScript. This component have only one attribute: type. This attribute is responsible for validation algorithm which will be applied at time when user presses a keyboard key.
At restoreView phase when initial view is created this attribute is set by JSF (actually by Facelets). This means that i have a component class with setter and getter for attribute 'type'. And a 'type' setter called with value specified in xhtml document.
Component object is recreated each time at restoreView phase if i specify them in reRender attribute. But when it is recreated my required attribute type is not set.
It's simply creates new component objects... and it's all. May be i don't understand something and this is normal behavior, but how to get attribute values in this case?
Code:
Simple test page:
<?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:u="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:r="http://richfaces.org/rich"
xmlns:v="http://nobodyhere.ru/jsf/validation">
<head>
<title>Test Page</title>
</head>
<body>
<h:form id="testForm">
<h:inputText id="textInput" value="test">
<v:keyValidator type="time"/>
</h:inputText>
<a:commandButton value="Make AJAX request" reRender="testForm"/>
</h:form>
</body>
</html>
Component class:
public class KeyValidator extends UIComponentBase
{
public KeyValidator()
{
System.out.println("new KeyValidator");
}
public KeyValidatorType getValidatorType()
{
return type;
}
public String getType()
{
return getValidatorType().toString();
}
public void setType(String type)
{
this.type = KeyValidatorType.valueOf(type.toUpperCase());
}
#Override
public String getFamily()
{
return KeyValidator.class.getName();
}
private KeyValidatorType type;
}
When i press "Make AJAX request" button my component is recreated. But attribute 'type' is not set in component.
The main problem starts at renderView phase in component renderer when encodeBegin is called it tries to get this attribute and of course it gets null instead of correct value.
So, the more precise question probably:
How to get attribute values of component on AJAX request at renderView phase?
Any help will be greatly appreciated.
You must override saveState and restoreState in component to save and restore needed attributes.
Good Luck!
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";
}
}
So i have a problem and i kindly need any info on how to resolve this.
We're using JSF 2.1 on JBoss 7.1 and we're using view scoped beans which have tables related to that view. The object represented as a row in that table is pretty big.
On every refresh of those views, a new instance of that bean is created.
To verify that this is happening, i have created a demo example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.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"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body>
<h:outputText value="#{viewScopedBean.i}" />
</h:body>
</html>
this template is then linked to a bean defined like this:
#ManagedBean
#ViewScoped
public class ViewScopedBean {
private int i = 0;
#PostConstruct
public void init(){
System.out.println("Init - " + i);
}
#PreDestroy
public void dest(){
System.out.println("Destroy - " + i);
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
Every time i refresh the view using the browser refresh button or by simply pressing enter in the browser address field, i clearly see the #PostConstruct method invocation.
If I leave the app alive for a very long time i see no #PreDestroy methods being called, and taking a heap dump shows me that ViewScopedBean has the same number of instances as the number i have reloaded the view, and the appear to remain on the heap even if i destroy the session.
This is a huge problem for me because if 500 users reloads the view with that large table, JBoss dies because it's heap space is full.
Is this the designed behavior of #ViewScoped beans or am i doing something wrong?
View scoped beans live only as long as a user interacts with a current view by returning postbacks to the same view (by returning null/void from UICommand action methods). Returning a current view id from an action method, firing a get request to the same view, refreshing the page, manually entering URL in browser's address bar and the events like these all cause the view to be recreated. Thus, you see view scoped beans reinstantiated on every such action.
ViewScoped beans are created every time the view created. So there it is.
But you complain that if 500 users come to the page JBoss will die. The only scope you can use to prevent that is ApplicationScope. But that is a bad idea.
Everybody will recommend you to use the narrowest scope.
So I think the solution to your problem is not another scope but a "lazy load" algorithm. You should load the table page by page. I don't think everybody will need the whole table at one read.
Here is a good description of scopes.
ManagedBeanScopes
If you want the bean to be destroyed you should choose requestScope. Because viewscoped beans stored in the session.
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">
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/
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.