I have two buttons, in one I need <f:convertDateTime> to work but in another I need to disable <f:convertDateTime> on button click.
I tried the attributes rendered and disabled, but it didn't work, which was my mistake as it is not available as per the API docs.
Also, is there a way to override the class javax.faces.converter.DateTimeConverter such that whenever f:convertDateTime is triggered my class will be called?
I tried the attributes rendered and disabled, but it didn't work, which was my mistake as it is not available as per the API docs.
Indeed, this behavior is not supported. However, as to a possible solution, you basically already gave the answer yourself:
Also, is there a way to override the class javax.faces.converter.DateTimeConverter such that whenever f:convertDateTime is triggered my class will be called?
That is possible and will also solve your initial problem. Just register it as <converter> in faces-config.xml on exactly the same <converter-id> as <f:convertDateTime>.
<converter>
<converter-id>javax.faces.DateTime</converter-id>
<converter-class>com.example.YourDateTimeConverter</converter-class>
</converter>
Therein you could do additional conditional checking, such as checking if a certain button is pressed, or if a certain request parameter is present or absent. If you'd like to continue the default <f:convertDateTime> job, just delegate to super provided that your converter extends from DateTimeConverter.
E.g. in getAsObject():
public class YourDateTimeConverter extends DateTimeConverter {
#Override
public void getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// ...
if (yourCondition) {
// Do your preferred way of conversion here.
// ...
return yourConvertedDateTime;
} else {
// Do nothing. Just let default f:convertDateTime do its job.
return super.getAsObject(context, component, submittedValue);
}
}
// ...
}
I'm guessing you have some text displayed based on a button click (correct me if I'm wrong). So it should be something like that:
<h:commandButton value="Convert" action="#{bean.doBtnConvert}" />
<h:commandButton value="Don't convert" action="#{bean.doBtnDontConvert}" />
<h:panelGroup id="pgText">
<h:outputText value="#{bean.someDateTime}" rendered="#{bean.convert}">
<f:convertDateTime pattern="dd.MM.yyyy HH:mm:ss" />
</h:outputText>
<h:outputText value="#{bean.someDateTime}" rendered="#{not bean.convert}"> />
</h:panelGroup>
And in bean you have the following field and methods:
private Date someDate;
private boolean convert;
public String doBtnConvert(){
setConvert(true);
String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
return viewId + "?faces-redirect=true";
}
public String doBtnDontConvert(){
setConvert(false);
String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
return viewId + "?faces-redirect=true";
}
// getter and setter for 'someDate' and 'convert' fields
Related
I'm using JSF 2.0 and I want to invoke a function defined in a Java controller when I click on an ace:textEntry.
I tried in this way:
<ace:textEntry readonly="true" value="#{myController.getValue()}"
onclick="#{myController.myFunc()}"/>
but when my page is open, the click event is called instantly.
So, I tried with:
<ace:textEntry readonly="true" value="#{myController.getValue()}">
<ace:ajax event="click" listener="#{myController.myFunc()}"/>
</ace:textEntry>
but my page is not rendered.
Is there another way to implement this behaviour ?
PS: I can use similar JSF components instead of ace:textEntry too.
First, you do not access getters directly in JSF for value backing - you access the property. Secondly, you should call the listener with the correct signature. To correct your example I would first rewrite the call like this,
<ace:textEntry readonly="true" value="#{myController.value}">
<ace:ajax event="click" listener="#{myController.myFunc}"/>
</ace:textEntry>
Then define MyController, like this;
#Named
#ViewScoped
public class MyController {
private value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public void myFunc(javax.faces.event.AjaxBehaviorEvent event) {
/* Do somethinig here... */
}
}
I have different objects which all implements the same interfaces. All of these objects need to be selectable by <p:selectCheckboxMenu/>. Default values for those objects and selected values are placed in same Map<?,?>, and few such Maps grouped together inside another Map. It sounds complicated but please look at the code below and all will be clear.
When I select an object, converter get a list of all objects from MyBean (CDI bean), looking by uuid required object and return it, without throwing any exception. The problem begins when I try to work with selected objects. For example this line of code inside:
onObjectChange() method from MyBean:
List<AllObjects> objects= objectContainer.getControllers().get("Object 1").get("selected");
throws an exception:
java.lang.ClassCastException: [Ljava.lang.Object; incompatible with java.util.List
And indeed when I hover mouse over objectContainer I see that it contains object of type selected=[Ljava.lang.Object;#dba1b6e7} But when I evaluate same line of code inside Expressions panel of Eclipse I get the required values: MyObject1#d8f0f5f8
I don't understand in general is it possible to do what am I doing, i.e. few different objects with same interface to be selectable by <p:selectCheckboxMenu/>. And if yes, why do I have this casting problem? My colleague said that it might be a problem with my converter and I tend to agree with she, but don't know is it correct and if yes, how to solve it.
UPDATE: It looks that problem not inside Converter but due to fact that I pass Collection to collect selected values dynamically, as a value of <ui:param/>. I pass it as List<AllObjects> and get it back as Object. I can cast it then to Object[] and every object inside it to appropriated object by using introspection and it works. But why it mutates initial object? It shouldn't do this.
Thank you in advance and my code below:
This is an interface:
public interfaces AllObjects{
public String getName();
}
There are multiple objects, MyObject, MyObject1, MyObject2 which implement interfaces above:
public MyObject implements AllObjects{
...
}
This is my bean and how my objects are initialized:
public MyBean {
Map<String, Map<String,List<AllObjects>>> objectContainer = new LinkedHashMap<String, Map<String,List<AllObjects>>>();
public void init(){
Map<String,List<AllObjects>> nameValuesPairs1 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects1 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects1 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects1);
nameValuesPairs.put("selected",allSelectedObjects1);
Map<String,List<AllObjects>> nameValuesPairs2 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects2 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects2 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects2);
nameValuesPairs.put("selected",allSelectedObjects2);
objectContainer.put("Object 1", nameValuesPairs1);
objectContainer.put("Object 2", nameValuesPairs2);
}
public void onObjectChange(){
...
List<AllObjects> objects= objectContainer .getControllers().get("Object 1").get("selected"); //throws exception
...
}
}
My *.xhtml page:
<h:panelGroup id="object_panel">
<ui:repeat id="objects_id" var="object" value="#{myBean.objectContainer.entrySet().toArray()}">
<p:selectCheckboxMenu
value="#{object.value['selected']}" label="#{object.key}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.value['default']}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="#this #parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
</h:panelGroup>
And converter:
public class ChartParameterConverter implements Converter, Serializable {
#Inject
private MyBean myBean;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
AllObjects result = null;
...
//here to result assigned MyObject1 or MyObject2 type depends on condition and it being returned
...
return result;
}
...
}
Ok, it looks that <ui:repeat> and <p:selectCheckboxMenu> not work well with DataModel which I used, Map<String,Map<String,List<MyObject>>>. I've changed my DataModel in the following way:
public ObjectContainer{
private String name;
private List<MyObject> defaultObjects;
private List<MyObject> selectedObbjects;
}
and pass to <ui:param> it as List<ObjectContainer>. so my *.xhtml page looks in the following way:
<p:selectCheckboxMenu
value="#{object.selectedObjects}" label="#{object.name}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.defaultObjects}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="#this #parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
And now everything works as it should.
And I throwed away my custom converter and use SelectItemsConverter from Omnifaces's library. Highly recommended change, code become much simpler and readable.
You could do a unique Tuple for each Object, The Tuple must be converted to unique String.. And you could have the posible values in a Array..
static Map<String,Object> uniques = new LinkedHashMap<>();
static{
//you could save the possible values in a Singleton Bean
uniques.put(key,value)...
}
In your Converter
getAsString -> Return a Key from Value
getAsObject -> Return a Value by Key
I have read a lot of posts at Stackoverflow but I didn't succeed in implementing the belowmentioned problem from my side.
the problem is: I need to type some text in <p:inputTextarea> and when clicking on button I need to get this value in the bean method.
I.e.:
<p:inputTextarea binding="#{input}"/>
<p:commandButton action="#{pessoaMB.adicionarContato(input.value)}" immediate="true"/>
with the bean method:
public void adicionarContato(String value) {
System.out.println(value);
}
The code I'm using gives me a null value.
I'm using #ViewScoped and cannot change this.
First of all, a side note: it is a bad practice to work with JSF components, you should work with model instead. I.e. don't use binding="#{input}", but stick to value="#{bean.text}".
Second, I doubt that immediate="true" is used appropriately in your setup. When used in a UICommand component like <h:commandButton> it will cause to skip JSF lifecycle for components with immediate="false" (or omitted, as it's the default), thus their value won't be set at all. Still, JSF will still preset submittedValue behind the scenes before the action method is executed.
Also, I strongly recommend to read BalusC's blog post Debug JSF lifecycle, as it is more than enlightening on the topic.
As to the solution, I'd suggest to deal with value binding with the bean, as presented in the first comment. With this approach you won't need action method parameter at all. Moreover, rethink your use of immediate attribute. If you think it's correct then you've got two choices: (1) use immediate="true" on <p:inputTextarea> or (2) switch to action="#{bean.action(input.submittedValue)}".
I would've done this :
<h:form>
<p:inputText value="#{pessoaMB.input}"/>
<p:commandButton value="add" action="#{pessoaMB.adicionarContato}" />
</h:form>
input would be here a pessoaMB property with a getter and setter (an IDE can autogenerate it).
private String input;
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
As for the adicionarContato method, it would be like this :
public void adicionarContato() {
System.out.println(input);
}
You should create a new class, i.e:
public class MyFields(){
String input1;
String input2; //and so on...
//getters and setters
}
Then, in pessoaMB create a property:
private MyFields inputFields; //getter and setter
Finally, in your xhtml file:
<h:form>
<p:inputText value="#{pessoaMB.inputFields.input1}"/>
<p:inputText value="#{pessoaMB.inputFields.input2}"/>
<!-- add more inputText components... -->
<p:commandButton value="add" action="#{pessoaMB.adicionarContato}" />
</h:form>
This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 1 year ago.
I'm relatively new to JSF and trying to learn how current JSF 2 applications are designed. I've seen reference to single page applications that use ajax. Can someone fill me in on some of the techniques used and / or point me to a model or book? The books I've seen (JSF Complete Reference etc.) are good for basic tech issues but I can't find a source for current design techniques.
Thanks
Dave
In order to implement your Single Page Application, you should state which piece of your page should be rendered. This can be accomplished making use of a boolean flag such as create, edit, list, and so on. For instance, see the following (Just relevant code)
<h:body>
<h:form rendered="#{userController.stateManager.create}">
<h:panelGroup rendered="#{not empty facesContext.messageList or userController.stateManager.failure}">
<!--render error message right here-->
</h:panelGroup>
<div>
<label>#{messages['br.com.spa.domain.model.User.name']}</label>
<h:inputText value="#{user.name}"/>
</div>
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
</form>
</h:body>
Notice that our form will be rendered when a flag create is true - See second line above. To wrap our flags, we create a classe named StateManager as follows
/**
* I am using lombok, which takes care of generating our getters and setters. For more info, please refer http://projectlombok.org/features/index.html
*/
#Setter #Getter
public class StateManager {
private boolean create;
private boolean edit;
private boolean list;
}
Now, because we are using only a single page, we should use a ViewScoped managed bean, which keep our managed bean scoped active as long as you are on the same view - Is it a single page application, right ? So, no navigation. With this in mind, let's create our managed bean.
#ManagedBean
#ViewScoped
public class UserController implements StateManagerAwareManagedBean {
private #Inject UserService service;
private #Getter #Setter stateManager = new StateManager();
private #Getter #Setter List<User> userList = new ArrayList<User>();
private #Getter #Setter User user;
#PostConstruct
public void initialize() {
list();
}
public void create() {
service.persist(user);
stateManager.setCreate(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void edit() {
service.merge(user);
stateManager.setEdit(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void list() {
userList = service.list();
stateManager.setList(true);
}
}
For each action method, we define which piece of our page should be rendered. For instance, consider that our form was processed, covering all of JSF lyfecycle, which implies that their values was successfully converted and validated, and our action method invoked. By using as example our create action method - see above -, we set its create flag as false because our form was converted and validated, so we do not need to show it again (Unless you want). Furthermore, we set both list and success flag as true, which indicates that the list of our page should be rendered and our form was successfully processed - You could use this flag to show something like "User created" such as bellow
<h:panelGroup rendered="#{userController.stateManager.success}">
#{messages['default.created.message']}
</h:panelGroup>
Now, let's discuss which piece of our page should be rendered when it is called for the first time. Maybe you do not know but a void method annotated with #PostConstruct will be called first. So we define which piece of our page should be rendered. In our example, we call list method, which sets its list flag as true and populate a backing list.
#PostConstruct
public void initialize() {
list();
}
Finally, let's review the following order nested within h:commandButton
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
First of all, you should call an ActionListener - here called StateManagerActionListener - which takes care of resetting any StateManager - code bellow. It must be called first before any other setPropertyActionListener designed to control any flag because the order defined within h:commandButton is the order in which they will be called. keep this in mind.
public class StateManagerActionListener implements ActionListener {
public void processAction(ActionEvent e) throws AbortProcessingException {
Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Map.Entry<String,Object> entry: viewMap.entrySet()) {
if(entry.getValue() instanceof StateManagerAwareManagedBean) {
((StateManagerAwareManagedBean) entry.getValue()).setStateManager(new StateManager());
}
}
}
}
StateManagerAwareManagedBean - used in our ViewScoped Managed bean -, which allows that we reset any StateManager of any ManagedBean instead of resetting one by one in our ActionListener, is defined as follows
public interface StateManagerAwareManagedBean {
StateManager getStateManager();
void setStateManager(StateManager stateManager);
}
Second, after defining our ActionListener, we use a setPropertyActionListener which set the flag which controls the enclosing piece of the view as true. It is needed because our form is supposed to be not converted and validated. So, in our action method, we set this flag as false as discussed before.
A couple of notes
User is marked as a RequestScoped ManagedBean so that it can not be injected into a ViewScoped one using a ManagedProperty because its scope is shother. To overcome this issue, i set its value by using a <f:setPropertyActionListener target="#{userController.user}" value="#{user}"> - See our form
Our example use JEE features which need a proper Application Server. For more info, refer http://docs.oracle.com/javaee/6/tutorial/doc/
ManagedBean can play different roles such as a Controller, DTO and so on. When it play a role of a Controller, i prefer suffix its name with Controller. For more info, refer http://java.dzone.com/articles/making-distinctions-between
EDIT: I have this snippet of code:
<h:inputText id="email_id" value="#{CreateUserManager.email}"
styleClass="#{CreateUserManager.emailPrimariaValid ? '' : 'inputErrorClass'}">
<f:validator validatorId="EmailValidator" />
<a4j:support event="onblur" reRender="email_id, messages" oncomplete="setAnchor();"
status="status4divCoverAll" ajaxSingle="true" />
</h:inputText>
This is the managed session bean:
public class CreateUserManager {
...
protected boolean emailPrimariaValid;
public CreateUserManager() {
...
this.emailPrimariaValid = true;
}
public boolean isEmailPrimariaValid() {
FacesContext context = FacesContext.getCurrentInstance();
UIInput input = (UIInput)context.getViewRoot().findComponent(":createUser:email_id");
return input.isValid();
}
public void setEmailPrimariaValid(boolean emailPrimariaValid) {
this.emailPrimariaValid = emailPrimariaValid;
}
}
Keep in mind that I remove this bean from session if I come from another page (url), so the bean execute the constructor again.
The problem: I write an invalid email and it sets correctly the class to inputErrorClass, but if I go to another page (so the input component is still invalid) and then come back to the first one, the class remains to inputErrorClass.
Are you by any chance using Seam? It has some good functionality for styling input elements when there are errors.
In Seam 2, you can use the <s:decorate> tag: http://docs.jboss.org/seam/2.2.0.GA/reference/en-US/html/controls.html#d0e28688
In Seam 3, you can use UIInputContainer and a composite component: http://jerryorr.blogspot.com/2011/10/replacement-for-sdecorate-in-seam-3.html
If you aren't using Seam... well, you can look at the Seam source code to see what they did!
One of the many approaches :
http://mkblog.exadel.com/2011/05/how-to-hightlight-a-field-in-jsf-when-validation-fails/