JSF id value to the component behaves different - jsf

I have the following code in my xhtml
<h:form id="xyForm">
<p:dataGrid id="xyGrid" ....>
<p:selectOneMenu id="code" ...> </p:selectOneMenu>
</p:dataGrid>
</h:form>
But when I saw the code generated, it looks like the following
<select name="xyForm:xyGrid:0:code_input" tabindex="-1" id="xyForm:xyGrid:0:code_input"> </select>
My question here is: why _input is getting appended with name and id.
As per my understanding id should be only xyForm:xyGrid:0:code not with appended _input. Could someone please clarify or tell me how to remove that _input?

An id attribute should be unique within an html page.
While rendering the SelectOneMenu, the select tag is wrapped inside a div tag. The div tag will have the id of the component i.e. xyForm:xyGrid:0:code, and so it makes sense that the select tag should have a different id.
Also, this is a common pattern in Primefaces, observed in other components like SelectBooleanCheckbox etc.
Instead of trying to removing _input, you will have to figure around how to work around it.

Related

Access JSF Custom Tag's id

TL;DR
Is there something like composite's cc.clientId that will give me the id of a custom tag?
Details:
I'd like a custom tag that will render a label, a value, and an icon. When the icon is clicked, a Bootstrap modal is supposed to open up that will edit the value.
<ui:composition>
<div> #{field.label}: #{field.value}
<a class="icon-pencil" data-target="#editForm" data-toggle="modal">
</div>
<h:form id="editForm" class="modal hide fade" role="dialog" ...>
... there's an input here linked to field.value ...
</h:form>
</ui:composition>
I can use the tag with <my:editor />. When I do so within a ui:repeat, an id is prepended to the editForm so that it renders with id j_idt104:editForm. So I need to modify the data-target to include the id.
This would be really easy with a composite component because I have access to the id via cc.clientId:
data-target="\##{cc.clientId}\:editForm"
However, I can't get it to work with a custom tag because I don't know of an EL expression (or something) that will give me access to the id. I could probably wait until after the page is loaded, then use jQuery to inspect the id and set the data-target after the fact, but I was hoping for a more pure-JSF solution.
I'm using JSF 2.1 and Bootstrap 2.3, at the moment.
Answer seems to be no. BalusC says you should replace things like custom tags with "normal" JSF components. In this case, that would mean using the composite component.
If you're deadset on using custom tag, I worked around the issue (with lots of help from the answer here) by using an index in the form's id:
I replaced the ui:repeat with a c:forEach that has access to the item's index:
<table>
<c:forEach items="#{bean.items}" var="item" varStatus="status">
<my:editor index="#{status.index}" ... />
</c:forEach>
</table>
And in the custom tag, I used the index in the data-target:
<tr>
<td>#{label}: #{value}</td>
<td>
<a data-target="\#editForm-#{index}" ... ></a>
<h:form id="editForm-#{index}" ... >
...
</h:form>
</td>
</tr>
The only catch is that c:forEach is a build-time construct (see details in this answer), which is a problem if you need any render-time data (like if you build information up in preRenderView). If you do, then you're better off using ui:repeat with a custom component, and relying on the cc.clientId.

how update/render <for:forEach with ajax

I got a list with steps which I displays as follow:
<ul >
<for:forEach id="steps" items="#{RecipeBean.recipe.steps}" var="step">
<li> <com:Step description="#{step.stepDescription}"/></li>
</for:forEach>
</ul>
<p:commandButton id="addstep" value="#{msg['step.add']}" action="#{RecipeBean.addStep}" update="steps" />
The button addStep adds a Step to the RecipeBean.recipe.steps collection. Now the new added step should be displayed. But like that I get the error message Cannot find component with identifier "steps" referenced from "j_idt8:addstep" and this is probably of the for:forEach component.
So does someone know how I can fix this issue?
The update attribute of the <p:commandButton> component should point to a component. Since <for:forEach> is not a component, but a taghandler, you receive an error message.
I would suggest you take a look at the existing PrimeFaces components like DataList, which presents a list objects in unordered format.
Then, you can refactor your code as follows:
<p:dataList id="steps" items="#{RecipeBean.recipe.steps}"
var="step" itemType="disc">
<com:Step description="#{step.stepDescription}" />
</p:dataList>
<p:commandButton id="addstep" value="#{msg['step.add']}"
action="#{RecipeBean.addStep}" update="steps" />

Javascript error in XHTML page in JSF 2

I have the following code in an xhtml page in JSF 2. But when the page loads I get an javascript error that
document.getElementById("country") is null or not an object.
Why is this happening?
<h:form>
<h:panelGrid columns="2">
Selected country locale :
<h:inputText id="country" value="#{country.localeCode}" size="20" />
Select a country {method binding}:
<h:selectOneMenu value="#{country.localeCode}" onchange="submit()"
valueChangeListener="#{country.countryLocaleCodeChanged}">
<f:selectItems value="#{country.countryInMap}" />
</h:selectOneMenu>
<script>
alert("hi");
document.getElementById("country").disabled=true;
</script>
</h:panelGrid>
</h:form>
It's not finding your component. Since your <h:inputText> is inside of an <h:form> the id will have the following pattern
formName:componentName. Since you did not specify an id for <h:form>, JSF will generate one for you. That's why you are seeing j_id1926454887_72d35e53:country where j_id1926454887_72d35e53 is the form id. If you want to deal with simpler id, then you should add an id value to your <h:form>. For example
<h:form id="form">
<h:inputText id="country" value="#{country.localeCode}" size="20" />
</h:form>
The id for <h:inputText> will now be form:country.
Even simpler, you could simply add prependId = "false" to your form
<h:form prependId="false">
and now your id for <h:inputText> will simply be country.
How can I get this id dynamically?
I'm going to modify your code slightly to achieve what you want (I'm thinking that you want to disable the input based on a specific event). Consider this simpler example
<h:form>
<h:inputText id="country" value="#{country.localeCode}" size="20" onclick="disableInputText(this.form)" />
</h:form>
Here I'm attaching a javascript function to an HTML DOM Event handler. In this case, I want the input to be disabled when I click on it (hence onclick). I'm also passing the form as a reference in the function. Then, define your Javascript like this.
<script>
function disableInputText(form) {
form[form.id + ":country"].disabled = true;
}
</script>
We simply grab the input in the javascript with the corresponding id via the object form and set its disabled attribute to true. (You can sort of view the form object as Map).
Also check the link below for more attributes of <h:inputText> and the different handlers you can use (the ones with on as prefix).
inputText Tag Attribute.
Also, check out the answer with the most votes to get a bigger picture on how ids are generated and other ways on how they can be determined.
How can I know the id of a JSF component so I can use in Javascript
It's seems to be a naming issue. the id of the field is not rendered as country.
i found a question that seems relevant. Composite components & ID

Conditional rendering in JSF [duplicate]

This question already has an answer here:
Ajax update/render does not work on a component which has rendered attribute
(1 answer)
Closed 7 years ago.
Hello I have this code to conditionally render components in my page:
<h:commandButton action="#{Bean.method()}" value="Submit">
<f:ajax execute="something" render="one two" />
</h:commandButton>
<p><h:outputFormat rendered="#{Bean.answer=='one'}" id="one" value="#{messages.one}"/></p>
<p><h:outputFormat rendered="#{Bean.answer=='two'}" id="two" value="#{messages.two}"/></p>
It gets the answer and renders the component but in order to see it on my page, I need to refresh the page. How can I fix this problem? Any suggestions?
The JSF component's rendered attribute is a server-side setting which controls whether JSF should generate the desired HTML or not.
The <f:ajax> tag's render attribute should point to a (relative) client ID of the JSF-generated HTML element which JavaScript can grab by document.getElementById() from HTML DOM tree in order to replace its contents on complete of the ajax request.
However, since you're specifying the client ID of a HTML element which is never rendered by JSF (due to rendered being false), JavaScript can't find it in the HTML DOM tree.
You need to wrap it in a container component which is always rendered and thus always available in the HTML DOM tree.
<h:commandButton action="#{Bean.method()}" value="Submit">
<f:ajax execute="something" render="messages" />
</h:commandButton>
<p>
<h:panelGroup id="messages">
<h:outputFormat rendered="#{Bean.answer=='one'}" value="#{messages.one}"/>
<h:outputFormat rendered="#{Bean.answer=='two'}" value="#{messages.two}"/>
</h:panelGroup>
</p>
Unrelated to the concrete problem, you've there a possible design mistake. Why would you not just create a #{Bean.message} property which you set with the desired message in the action method instead, so that you can just use:
<h:commandButton action="#{Bean.method()}" value="Submit">
<f:ajax execute="something" render="message" />
</h:commandButton>
<p>
<h:outputFormat id="message" value="#{Bean.message}" />
</p>
I know it's not the central point of the question, but as I had this problem many times in the past, I just post it here to help others who are in need.
For those who uses PrimeFaces there's a component in PrimeFaces Extension called Switch.
Sometimes you need to display different outputs or components depending on a value. Usually you can achieve this by using the ui:fragment tag. With the pe:switch util tag you won't have to declare ui:fragment tags, with different checks like ui:fragment rendered="#{!empty someController.value}", anymore.
style="visibility: #{questionchoose.show==false ? 'hidden' : 'visible'}"

Seam / RichFaces + forceId

I'm having trouble with a4j:repeat and it's id generation. Every element inside the loop has it's id preceded with a unique identifier. I don't want this. I want certain elements to contain the id that I present them (I'm ensuring they're unique).
I've search around and it looks like Tomahawk tags have an attribute forceId which will cause the element to use the id provided. The only thing is, it looks like it's not recommended to use Tomahawk tags with Seam / RichFaces.
Is there anything similar in anyone of the tag libraries recommended to be used with Seam? Barring that, is it reasonably feasible for me to subclass a4j:repeat (or possibly even ui:repeat) and change the way it handles id generation?
Any ideas? All I need is a way to loop through elements giving them dynamic ids.
This is not a RichFaces problem. It is JSF. JSF adds a unique id to each components. This is a known JSF feature.
However, there is something you can do.
In your <h:form> you can set prependId="false". This will tell JSF not to add any ID's from each component.
(Also make sure you are not using s:decorate="/layout/template.xhtml because the template.xhtml and edit.xhtml will add id's of their own.
So do something like this:
<h:form prependId="false">
<a4j:repeat value="#{foo}" var="f" rowKeyVar="row">
<h:inputText id="unique#{row}"/>
</a4j:repeat>
</h:form>
This will make the id like this: unique1 unique2 unique3 etc
Update
Seems you are right. For some reason, the id tag doesn't support this type of EL expression.
I tried the following:
<a:repeat id="table" value="#{foo}" var="k" rowKeyVar="row">
<h:inputText id="test#{row}" value="row is #{row}" styleClass="test#{row}"/><br/>
</a:repeat>
And it produces the generated html
<input type="text" class="test0" value="row is 0" name="table:0:test" id="table:0:test">
<input type="text" class="test1" value="row is 1" name="table:1:test" id="table:1:test">
<input type="text" class="test2" value="row is 2" name="table:2:test" id="table:2:test">
So as you can see, I still get a unique id because. Probably is adding the number for me automatically.
It doesn't matter if you add or not. The result is the same.
Maybe useful: You can get real ids in JSF (RichFaces) with #{rich:clientId('id')}. So you can use the generated id in JS.
I don't know why you wouldn't let JSF do the ID generation for you, but I understand that it isn't easy to figure out, since JSF has very little documentation on this subject in my opinion.
I'll tell you my findings on this subject by giving an example (using JSF 2.0.3 Mojarra by the way):
<h:form id="myForm">
<ui:repeat id="loopzor" var="#{myItem}" value="#{myController.myList}">
<h:outputLabel for="myName" value="#{labels.name}:" />
<h:inputText id="myName" value="#{myItem.name}" />
</ui:repeat>
<h:selectOneMenu id="type" value="#{address.type.id}">
<f:selectItems value="#{types}" var="type" itemLabel="#{type.label}" itemValue="#{type.id}"/>
</h:selectOneMenu>
<h:inputText id="value" value="#{address.value}"/>
</h:form>
This is a typical example of how a form might look like. This is what happens when the page is rendered with a list size of 2 items:
<form id="myForm">
<label for="myForm-loopzor-0-myName">Name:</label>
<input id="myForm-loopzor-0-myName" type="text" value="someName" />
<label for="myForm-loopzor-1-myName">Name:</label>
<input id="myForm-loopzor-1-myName" type="text" value="someName2" />
<select id="myForm-type" name="myForm-type>
<option value="1" selected="selected">Label1</option>
<option value="2" >Label2</option>
</select>
</form>
So the ID generation is as follows:
Give IDs to all h:form elements, this will prefix all elements in this form with this ID
If you don't want the form ID prefixed to each element, add the attribute prependid="false" to the h:form element
Give IDs to all ui:repeat elements (even though the id attribute is not specified in the specs)
Give IDs to all h:inputText and other input elements, even inside ui:repeat elements they will get a unique ID based on their position in the loop
You can give IDs to f:selectItems elements but they will not be taken into account
You can suffix the IDs with the varStatus object but this will always return an empty string, probably because when generating the dynamic IDs the varStatus object does not exist
I hope this can clear out some of the confusions about ID generation!

Resources