I need to determine the ID of a form field from within an action handler. The field is a part of a included facelets component and so the form will vary.
included.xhtml
<ui:component>
<h:inputText id="contained_field"/>
<h:commandButton actionListener="#{backingBean.update}" value="Submit"/>
</ui:component>
example_containing.xhtml
<h:form id="containing_form">
<ui:include src="/included.xhtml"/>
</h:form>
How may I determine the ID of the form in the update method at runtime? Or better yet, the ID of the input field directly.
Bind the button to your backing bean, then use getParent() until you find the nearest form.
Programmatically I would use jsight's method. You can know the id of your elements (unless you let JSF create them, I don't know the means for numbering in the ids) by looking at it. h:form is a naming container so as long as you don't have it wrapped in another naming container it will be containingForm:containedfield The ':' is the naming separator by default is JSF and the ids are created like this, roughly anyway, (parentNamingContainerId:)*componentId
Since update method is of type actionListener, you can access your UI component as follows
public void update(javax.faces.event.ActionEvent ac) {
javax.faces.component.UIComponent myCommand = ac.getComponent( );
String id = myCommand.getId(); // get the id of the firing component
..... your code .........
}
Related
I have a page where I want to include a part of the page (footer in this instance) dependant on values given from a view parameter.
I have my ViewScoped backing bean initializing on preRenderView
<f:metadata>
<f:viewParam name="racecode" value="#{displayResults.racecode}" />
<f:event type="preRenderView" listener="#{displayResults.init}" />
</f:metadata>
This queries the database to get the name of the footer to be included. This then, is used in this fashion :
<h:panelGroup id="customFooter" display="block">
<ui:include src="#{displayResults.customFooter}" />
</h:panelGroup>
This always gives me a missing page. But if I enter the page name manually it works. Same if I replace the ui:include with an h:outputText.
I understand that it has something to do with the phases of JSF and that at the time the ui:include is done, the value is not set yet. (reading up and better understanding the phases is something on my TODO list).
The question remains. How can I get something of the sort done. Have a bean use the viewParam, query the database and use that value in a ui:include?
#wemu has already explained the cause. The <ui:include src> is evaluated before init() method is called. His proposed <f:phaseListener> solution is however clumsy.
Just use #ManagedProperty/#PostConstruct on a #RequestScoped bean.
#ManagedProperty("#{param.racecode}")
private String racecode;
#PostConstruct
public void init() {
// ...
}
PreRenderView listeners are called within the RenderResponsePhase, before components are rendered BUT AFTER the TagHandlers are called. This means that TagHandlers will NOT see data initialized within a PreRenderView event.
If you are using a <ui:include value="#{myBean.myViewId}" /> to dynamically switch an include you can't use a PreRenderView event listener to set the myViewId property of myBean.
If you need to do that use a <f:phaseListener>.
before I have been using render time tags(like a4j:repeat,ui:repeat), to create panels based on a collection of items. All of that changed and now I must use c:forEach instead.
On each panel I do a lot of AJAX calls and partial updates of the page. That's why I'm using custom id's in order to identify wich components I want to update. So my render attributes are like this:
#{cc.clientId}:pnlRepeat:#{row}:radioAplicar
Where pnlRepeat is the id attribute, and {#row} is the rowKeyVar attribute in the same tag. Now... none of them exists when using c:forEach, hence, I'm getting duplicated id exceptions. I know I could use the varStatus, and create a panel with the #{row} Id, but on the other hand. JSF doesn't let id attributes to evaluate an EL expression. What would be a workaround?. Thanks a lot.
You should not be trying to base any of your ManagedBean logic or EL expressions on the generated IDs of repeating components like <ui:repeat> or <c:forEach>. Like you already mentioned the EL expression will not let you dynamically evaluate an Id expression, so the appropriate way to deal with these situations where an event is triggered by an individual item within a repeating component is to pass a uniquely identifying value in the form of the <f:attribute> tag.
Using the <f:attribute> tag will put the specified value into the request attributes so that it can be retrieved in your action listener.
<ui:repeat value="..." var="repeatedVar">
<h:inputText id="newTxt" value="#{repeatedVar}" >
<f:attribute name="unique_id" value="#{repeatedVar.uniqueId}" />
</h:inputText>
</ui:repeat>
<h:commandButton actionListener="#{managedBean.someMethod}" ...
And in the action method we can determine what the action or business logic is to be executed on by retrieving that dynamic components attribute.
public void someMethod() {
String uniqueId = (String) UIComponent.getCurrentComponent(FacesContext.getCurrentInstance()).getAttributes().get("unique_id");
//Get the unique data object
//Do some business logic and other stuff...
}
I want to add a label with an id and an inputfield with an id to a panelgroup (layout box), which has its own id.
I expect the label and the inputfield to have the following ids: (viewid):(formid):(panelgroupid):(own id)
But somehow the panelgroupid (fachlich1) isnt passed on to the children.
<form id="viewns_Z7_6PQJDK4JGD09F0I34QTLQH2015_:j_id_3" …>
<div id="viewns_Z7_6PQJDK4JGD09F0I34QTLQH2015_:j_id_3:fachlich1">
<label id="viewns_Z7_6PQJDK4JGD09F0I34QTLQH2015_:j_id_3:label" for="viewns_Z7_6PQJDK4JGD09F0I34QTLQH2015_:j_id_3:value">Bla</label>
<input id="viewns_Z7_6PQJDK4JGD09F0I34QTLQH2015_:j_id_3:value" type="text" value="Blubb34534" name="viewns_Z7_6PQJDK4JGD09F0I34QTLQH2015_:j_id_3:value">
</div>
</form>
Any clue what I might be doing wrong? :(
Thanks for your help!
Edit:
i want to create my own component which basically consits of a surrounding div + label + inputtext.
i extend from the HtmlPanelGroup and override the encodeBegin method, where i set the layout to block, set the id of the panelgroup,add the components, after which i call super.encodeBegin:
public void encodeBeginn(FacesContext context)
{
setLayout("block");
setId(getId());
HtmlOutputLabel label = new HtmlOutputLabel();
label.setId("label");
label.setFor("value");
label.setValue(getLabel());
getChildren().add(label);
HtmlInputText text = new HtmlInputText();
text.setId("value");
text.setValue(getValue());
getChildren().add(text);
super.encodeBeginn(context)
}
Only implementations of NamingContainer interface will prepend its own ID to the ID of their children. As per the javadoc it are the following components:
All Known Implementing Classes:
HtmlDataTable, HtmlForm, UIData, UIForm, UINamingContainer
The HtmlPanelGroup, as backed by <h:panelGroup>, is not among them. In standard JSF, only <h:dataTable> and <h:form> implement it. In standard Facelets, only <ui:repeat> implements it as well (through UINamingContainer).
This should not form any technical problem at all. If you think that your concrete problem (which you didn't tell anything about in your question) is caused by this, then you've likely found the wrong cause and the cause of your concrete problem has to be sought in a different direction.
Specify explicitly id in <h:form> and set prependId=false in h:form
As you mentioned in you comment on BalusC answer, it would be possible to have the custom component implement NamingContainer. I tried something similar and since your custom component is basically nothing more that a composite-component your surrounding HtmlPanelGroup will be the NamingContainer for the internal composed components.
Be carefull though and do not override getFamily with another name. This drove me nuts until I figured out that when you do that, JSF cannot find the standard renderer (the one registered with HtmlPanelGroup).
public class MyComponenten extends HtmlPanelGroup implements NamingContainer {
public void encodeBeginn(FacesContext context){
...fill with components
super.encodeBeginn(context);
}
...
I am having problems with ValueChangeListener attached to a dropdown list.
Here is the code:
<h:selectOneMenu
value = "#{MultiFileSelectMgmtBean.selectedLocationName}"
valueChangeListener = "#{MultiFileSelectMgmtBean.LocationChangeEvent}"
onchange = "submit();"
>
<f:selectItems
value = "#{MultiFileSelectMgmtBean.locationsListItems}">
</f:selectItems>
</h:selectOneMenu>
And here is the backing bean:
protected List<SelectItem> locationsListItems;
...
public void LocationChangeEvent( ValueChangeEvent vce ) throws Exception
{
selectedLocationName = (String) vce.getNewValue();
}
The problem is that 'selectedLocationName' gets a "11" or "13" value, even the dropdown list is populated with two strings "LocationTest1" and "LocationTest2".
What could be the problem with vce.getNewValue?
The submitted value of the dropdown list is the option value, not the option label as you seem to think. Note that the method is also called getNewValue(), not getNewLabel(). The option labels are not sent over HTTP from client to server by the HTML form submit. There's no way to extract them from the HTTP request.
If you really need the option label instead of the option value for some unclear reason, then you'll either need to use it instead of option value while creating the select items, or to have a mapping of all option labels associated with the option values somewhere, so that you can get the label by the value from this mapping. The chance is big is that you already have this sort of mapping in your bean, otherwise you wouldn't be able to populate the <f:selectItems> value :)
See also:
How to get both label and value from f:selectItems
Our <h:selectOneMenu> tag wiki page
Unrelated to the concrete problem: the combination of a <h:selectOneMenu>, a valueChangeListener and onchange="submit()" indicates that you're using a JSF 1.x specific hack in order to achieve the functional requirement of populating another dropdown or fields based on the change of the dropdown. Since you seem to be already using JSF 2.x, I recommend you to forget about this approach at all and just use <f:ajax listener> instead. The aforelinked wiki page contains one example.
I want to reset JSF inputs to their original managed bean values after validation failed.
I have two forms inside the same page - the first form has a commandLink to initialize the second form. The second form is rendered as a dialog whose visibility is toggled through jQuery - for the purpose of this exercise, though, I can illustrate just with two forms on the same page. Also, while I'm using PrimeFaces 2.2.x in my app, the same behaviors appear with regular h:commandLink as well.
The issue I'm having is:
click link in first form to initialize second form
submit invalid values in second form
click link in first form again to initialize second form - invalid values still there and/or UIInput state is still invalid.
For example - take the following form
<h:form id="pageForm">
<h:commandLink actionListener="#{testBean.initialize}">Initialize, no execute
<f:ajax render=":dialogForm"/>
</h:commandLink>
<br/>
<h:commandLink actionListener="#{testBean.initialize}">Initialize, execute=#this
<f:ajax execute="#this" render=":dialogForm"/>
</h:commandLink>
</h:form>
<h:form id="dialogForm">
<h:messages/>
String property - Valid: <h:outputText value="#{property.valid}"/>
<br/>
<h:inputText id="property" binding="#{property}" value="#{testBean.property}">
<f:validateLength minimum="3"/>
</h:inputText>
<br />
Int property - Valid: <h:outputText value="#{intValue.valid}"/>
<h:inputText id="intValue" binding="#{intValue}" value="#{testBean.intValue}">
<f:validateLongRange maximum="50" />
</h:inputText>
<br/>
<h:commandLink actionListener="#{testBean.submit}">
Submit
<f:ajax render="#form" execute="#form"/>
</h:commandLink>
<h:commandLink actionListener="#{testBean.initialize}">Initialize, execute=#this
<f:ajax execute="#this" render="#form"/>
</h:commandLink>
</h:form>
Bean class:
#ManagedBean
#ViewScoped
public class TestBean {
private String property = "init";
private Integer intValue = 33;
// plus getters/setters
public void submit() { ... }
public void initialize() {
intValue = 33;
property = "init";
}
}
Behavior #1
click either "Initialize" link on the pageForm
inputs get initialized to "init", "33"
now submit something invalid for both fields like "aa", "99"
now click any of the "initialize" links again (they all seem to behave the same - makes no difference whether it's in the same form or different, or whether I have specified execute="#this" or not.)
Result => UIInput.isValid() = false, both values reset though ("init", "33").
Expected => valid = true (or is this not reasonable to expect?)
Behavior #2
click either "Initialize" link on the pageForm
inputs get initialized to "init", "33"
now submit something invalid for the text field but valid for the int field ("aa", "44")
now click any of the "initialize" links again
Result => "init", valid=false; 44, valid=true
Expected => "init", valid=true; 33, valid=true
I have also looked at:
JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean
and
How can I populate a text field using PrimeFaces AJAX after validation errors occur?
The suggestion to explicitly reset the state of UIInputs with resetValue does work, but I'm not happy with it.
Now, I sort of understand why the isValid is not resetting - my understanding of the JSF lifecycle is that once a value is submitted to a component, isValid is not reset until the component is successfully submitted and validated and the Update Model Values phase sets the bean value. So there may be no way around explicitly resetting the valid state in this case, since I want to use #{foo.valid} for conditional CSS styling.
What I don't understand, though, is why the components that successfully validated are not re-initializing from the bean. Perhaps my understanding of the JSF lifecycle is slightly off?
I understand the rules layed out in the answer to How can I populate a text field using PrimeFaces AJAX after validation errors occur? as they pertain to an individual component but not to the form as a whole - i.e., what happens if a component succeeds validation but the validation overall fails?
In fact, there may turn out to be no better way than explicitly calling resetValue on components. In my case, all of the dialogs are in the same big JSF view tree with the underlying page that opens them. So from JSF's perspective, the same view component state including invalid input values should be preserved until we navigate away from the view, as it has no visibility into how we're toggling display attributes client-side.
The only other thing that might work is if the components that make up the dialog are actually not rendered in the JSF view tree unless they're visible. In my case, they're always rendered, using CSS to toggle visibility.