I have an entity which i display and modify in an xhtml page. This entity has a getter returned a joined entity using a getter with a parameter (the language):
public JoinedEntity getJoinedEntity(Locale locale){
for(JoinedEntity je: joinedEntities)
if(je.getLocale().equals(locale)
return je;
}
my xhtml cointains the following code:
<h:inputText value="#{myBean.myEntity.getJoinedEntity(localeBean.locale).mytext}"/>
There is no problem if i only display this entity but when i try to save the modified JoinedEntity (using TextInputs), i receive an EL exception telling me that there is no getJoinedEntity argument in my Entity.
Is it a possibility to create such a setter:
public void setJoinedEntity(Locale locale, JoinedEntity je){
...
}
Is there another way to solve that?
I found the solution to my problem. I deleted my getter and used a map as follow:
<h:inputText value="#{myBean.joinedEntities[localeBean.locale].myText}">
public Map<Locale,JoinedEntity> getJoinedEntities(){
return joinedEntities;
}
public void setJoinedEntities(Map<Locale,JoinedEntity> joinedEntities){
this.joinedEntities = joinedEntities;
}
Updating the map works then fine.
Related
This question may be more of the type "conceptual" or "I don't understand JSF".
My scenario:
I have a JSF Page (index.xhtml) where I use a p:accordionPanel (but I don't think it matters what component it is). What I want to do is to set the activeIndexes of it.
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever')}">
// bla bla...
</p:accordionPanel>
And the (simplified) method in the backing bean:
public String getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return activeSections;
}
Now this works just fine on a normal page load.
But if I click on a p:commandButton (with ajax=false) (or anything else which "sends" data back to the server I guess) - I get the following exception:
/WEB-INF/tags/normalTextSection.xhtml #8,112 activeIndex="#{myController.getActiveIndexesForSections(name)}": Illegal Syntax for Set Operation
// bla..
Caused by: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation
After some googling / reading the error message I found that I need a setter.
First of all: I don't want a setter - do I really need one or is there a way to tell JSF I don't want this "behavior".
Second I realized that it's not that "easy" to provide a setter, because my method has a parameter (so public void setActiveIndexesForSections(String name, String activeIndexes) or public void setActiveIndexesForSections(String name)won't work).
What I came up with in the end is:
Create a (generic) "Pseudo-Property-class":
// just a dummy class since the class is recreated at every request
public class Property<T> implements Serializable {
private T val;
public Property(T val) {
this.val= val;
}
public T getVal() {
return val;
}
//no need to do anyhting
public void setVal(T val) {
}
}
Change the bean method:
public Property<String> getActiveIndexesForSections(String holderName){
String activeSections = "";
for(Section s : sectionMap.get(holderName)){
if (s.isActive())
//add to the string
}
return new Property<String>(activeSections);
}
And call it from the index.xhtml:
<p:accordionPanel multiple="true" activeIndex="#{myController.getActiveIndexesForSections('whatever').val}">
// bla bla...
</p:accordionPanel>
This works but obviously is a ugly hack/workaround.
What is the proper way to handle a situation like this? Or is what I'm doing simply completely wrong?
The setter is needed to remember the active indexes as they were when the form is submitted. Basically, you need to bind it as a value expression (with a property), not as a method expression (like an action method), nor to an unmodifiable collection (like activeIndex="#{param.tab}"). Exactly like as with input values. Technically, you're indeed doing it "simply completely wrong" ;)
The requirement is however understood. Given that you're really not interested in the changed active indexes, and thus want to reset them to defaults on every form submit, then you can bypass it by storing the result as a request attribute with help of <c:set>. This way you will fool EL to set it in the request attribute map instead of the intented bean property.
<c:set var="activeIndex" value="#{myController.getActiveIndexesForSections('whatever')}" scope="request" />
<p:accordionPanel multiple="true" activeIndex="#{activeIndex}">
<!-- bla bla... -->
</p:accordionPanel>
Under the covers, it will basically do externalContext.getRequestMap().put("activeIndex", value) as setter operation, which will obviously just work.
Update: upon inspecting the source code of AccordionPanel component, I saw another workaround given the fact that the activeIndex won't be set when the rendered attribute evaluates false. So just alter the rendered attribute to behave exactly that: evaluate false during update model values phase (the 4th phase).
<p:accordionPanel multiple="true"
activeIndex="#{myController.getActiveIndexesForSections('whatever')}"
rendered="#{facesContext.currentPhaseId.ordinal ne 4}">
<!-- bla bla... -->
</p:accordionPanel>
I want to use a backing component as a layer for accessing the attributes of my composite component (as defined in its interface).
What I wanted to achieve was reading the attributes of my componentent via my backing component class where i give back the property value of the attribute provided.
public String getName() {
if (this.name == null) {
this.name = getAttributes().get("name");
}
return this.name;
}
But when setting a new value e.g. via an input field I wanted to store the value only within my backing bean properties not updating the values of the original properties passed as attribute arguments to my composite component.
public void setName(final String name) {
this.name = name;
}
My problem now is when the getter of my backing component is called the first time or at some early stage of his life the code of the getter as shown above results in a Stackoverflow exception as getAttributes.get("name") calls the getter of my backing component (itself) instead fetching the property/attribute provided to my composite component.
Fun part is using a simple getter only returning this.name instead of calling getAttributes() I can set a breakpoint there and then calling getAttributes.get("name") (via debugger) results not in a overflow/ calling its own getter but instead the attribute provided to my composite component is returned.
I guess it has something to do with the coupling betweend the backing component and the composite component. That when the getter gets called for the first time no coupling between them is given and therefor the call of getAttributes.get("name") results in calling the getter of my backing component whereas later the call does not invoke its own getter but instead fetches the attribute provided to my comp component.
Anyone have any idea how to solve this issue?
Thnx in advance.
UIComponent#getAttributes() is a very special map. On a given key, say "foo", it first evaluates #{component.foo} as a ValueExpression which implicitly invokes UIComponent#getFoo() method. See also the javadoc. That totally explains the infinite loop. If the getFoo() method was absent, then it'd just have continued to look in the "static" map (which you can control by overriding UIComponent#getValueExpression()).
You need to solve your concrete functional requirement differently. I have actually had a hard time in wrapping my head around your concrete functional requirement, but I believe that you actually need to define separate properties with separate getters/setters, all properly delegating to the state helper:
public String getLocalName() {
return getStateHelper().eval("localName", getAttributes().get("name")); // Note: this thus defaults to #{cc.attrs.name} when not set before.
}
public void setLocalName(String localName) {
return getStateHelper().put("localName", localName);
}
and then use it in the composite implementation as
<h:inputText value="#{cc.localName}" />
instead of
<h:inputText value="#{cc.attrs.name}" />
i try to set normal property by menu-item, but it is does not work.
jsf:
<p:menuitem value="Names"
url="/master.xhtml"
action="#{navigation.name}">
<f:setPropertyActionListener target="navigation.name"
value="Billy" />
</p:menuitem>
ManagedBean:
#ManagedBean(name="navigation")
#SessionScoped //
public class LinksNavigation {
public LinksNavigation() {
super();
this.milchFleisch = "./menuFleisch.xhtml";
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name= name;
}
}
But it does not work. I dont set with "f:setPropertyActionListener"
value = "Bully". How can i set property Value by menuItem??
Thanks
In setPropertyActionListeneryou have a String instead of an EL expression. Change
target="navigation.name"
to
target="#{navigation.name}"
Also, you need to take a closer look to at the tag documentation for p:menuitem. The attribute action is expecting a method expression. You will get a MethodNotFoundException with the way your code is set up. You need to change
action="#{navigation.name}"
to
action="#{navigation.getName}"
The abbreviated syntax #{navigation.name} is used for value expressions.
You are mixing navigation methods. You cannot have the attributes url and action together for the same p:menuItem. When I tested your code action and setPropertyActionListener were never called when url was present. This behavior suggest that you have to pick one or the other.
COMMENT
Based on your sample, I think you should rethink your approach. Let's pretend for a moment that you decided not to use url="/master.xhtml" and let's go over the sequence of events. After you press the menu item, the expression #{navigation.name} in setPropertyActionListener will first set the value "Billy" to your name property (e.g. navigation.setName("Billy")). Then the expression #{navigation.getName} in the attribute action will call getName(). Now action will have the String value "Billy" and it will try to navigate to Billy.xhtml (assuming no navigation rules in faces-config and you have JSF 2.0). If that's the behavior you're after or if you have rules set up in faces-config and you are not hardcoding the value than that's another story entirely. Just keep this in mind.
I spent already more time as is good about some saving or updating issue in inputtext field, i go right to the point:
i have basic single input text with some basic attributtes
<h:inputText id="name" value="#{salesController.selectedSalesName}" />
here is a getter for inputText value
public String getSelectedSalesName(){
for(DealerListView dealer : dealerList){
if(dealer.getDealerId() == getSelectedDealerId()){
return dealer.getName();
}
}
return "";
}
nested in there i use ajax tag
<f:ajax event="change" render="name" listener="#{salesController.updateSelectedSalesName()}" />
a here is back bean method for updating a input text field
public void updateSelectedSalesName() {
DealerData dealDat = BeanFactory.getHotelDAOService(DealerData.class).findOne(selectedDealerId);
dealDat.setName(name);
BeanFactory.getHotelDAOService(DealerData.class).update(dealDat);
}
whole result of this is stack trace which say
value="#{salesController.selectedSalesName}": Property 'selectedSalesName' not writable on type sk.hotel.web.controller.SalesController
I know that something changes is need for that getter method but try some combinations without result which make corect update of value to database.
(I dont use any commands buttons for submit,update only response on pressing Enter in that inputText field.)
I want some guide how can be modified this save/update process whether on back-bean or jsf layout
or maybe someone solved similar situation already,and can share his solution.
Thanks all of you for advice posts.
Regards and nice day
First, add a field:
String selectedSalesName;
Add a setter and setter:
public String getSelectedSalesName() {
return selectedSalesName;
}
public void setSelectedSalesName(String selectedSalesName) {
this.selectedSalesName = selectedSalesName;
}
Add a ajaxListener(AjaxBehaviurEvent event) to create a new Dealer or Update current Dealer
public void ajaxListener(AjaxBehaviorEvent event) {
Dao dao = BeanFactory.getHotelDAOService(DealerData.class)
if (selectedDealerId == null) {
DealarData dealerData= new DealerData();
dealerDate.setName(getSelectedSalesName());
dao.add(dealerData);
setDealer(dealerData);
} else {
DealerData dealDat = dao.findOne(selectedDealerId);
dealDat.setName(name);
dao.update(dealDat);
}
}
A setter to the current dealer
int selectedDealerId;
public void setDealer(DealerData dealer) {
selectedDealerId = dealer.getId();
selectedSalesName = dealer.getName();
}
And the xhtml page:
<h:inputText value="#{salesController.selectedSalesName}" id="idSalesInput">
<a4j:ajax event="keyup" listener="#{salesController.ajaxListener}"
execute="idSalesInput"/>
</h:inputText>
Change "keyup" for the event you want to listen.
When you press a key, the listener is called, and the value of idSalesInput is submitted (the setSelectedSalesName() method is called here, for this reason you got the Property 'selectedSalesName' not writable exception),and the listener create or update a new DealerData.
Have a nice Day and sorry for my bad english!
Binding value in your inputText is two way, when it is rendered than getter is called to calculate value, when it is submited (like in your AJAX event) setter is called for that property to set value in your backing bean. So JSF tries to call setSelectedSalesName(String value). JSF can't write your property, which means can't call setter.
See also:
AJAX listener not being fired for inside
I created a simple HtmlInputText
<h:inputText binding="#{IndexBean.objUIInput}" />
Then in my managed bean, it is :-
private UIInput objUIInput;
public UIInput getObjUIInput() {
objUIInput.setValue("laala");
return objUIInput;
}
public void setObjUIInput(UIInput objUIInput) {
System.out.println("Set!!");
this.objUIInput = objUIInput;
}
But i always get NullpointerException. Do i need to do anything extra on my JSF page? like we do jsp:usebean setproperty? Please help me.
Whenever you'd like to change the component's default state/behaviour prior to display, then you need to instantiate it yourself. I.e. during declaration:
private UIInput objUIInput = new HtmlInputText();
or during construction:
public Bean() {
this.objUIInput = new HtmlInputText();
}
or, as Bozho suggested, using #PostConstruct:
#PostConstruct
public void init() {
this.objUIInput = new HtmlInputText();
}
(which will take place after construction of the bean and initialization/setting of all managed properties).
And indeed, you should preferably not do any business logic in getters/setters. They are to be used to access bean properties and they can be called more than once during bean's life.
As per the comments, you can alternatively also move the UIInput#setValue() call to the setter method. JSF will call it once directly after precreating the component.
public void setObjUIInput(UIInput objUIInput) {
this.objUIInput = objUIInput;
this.objUIInput.setValue("laala");
}
When you bind a component, the getter and setter are supposed to be simple - only get/set - no logic inside them.
Perhaps the JSF lifecycle is calling the getter to check whether it needs to instantiate the component, and the getter initially would throw a NPE.
Remove all logic from your getter, or at least add a null check.
Actually, I'd advice for not using binding at all.
If you want to set an initial value to your component, use a method annotated with #PostConstruct and assign the value there, then use the value attribute.