I have a page where the user can upload a file to the server. He can set which groups/users can read/write (to) the file.
This is done by selects. One select holds the names of the groups the user is currently in, and on the same row is a select with options Read and Write. On the second row is the corresponding stuff for selecting users.
When the user selects a group and either Read or Write for said group, he then presses a button. After pressing the button I would like to show the user a list of permissions below the select. The list would have a group name, access setting for said group and a button to remove the permission item.
My problem is, I'm not able to update the page and show the list of permissions. Server side the permissions are added to an ArrayList, but I can't get the contents to show in a4j:repeat. How can I get the repeat tag to update?
Here is what I've been trying to do so far:
<h:form>
<h:selectOneMenu value="#{assetUploadForm.currentGroupName}" valueChangeListener="#{assetUploadForm.groupNameChanged}">
<f:selectItem itemValue="users" itemLabel="users" />
<f:selectItem itemValue="foobar" itemLabel="Foobar" />
<a4j:ajax event="valueChange" render="second" execute="#this" />
</h:selectOneMenu>
<h:selectOneMenu value="#{assetUploadForm.currentGroupRW}" valueChangeListener="#{assetUploadForm.groupRWChanged}">
<f:selectItem itemValue="R" itemLabel="Read"/>
<f:selectItem itemValue="W" itemLabel="Write"/>
<a4j:ajax event="valueChange" render="second" execute="#this" />
</h:selectOneMenu>
<a4j:commandButton action="#{assetUploadForm.addGroupRights}" value="add group" render="groupList"/>
<ul>
<a4j:repeat value="#{assetUploadForm.groupPermissions}" var="permission" id="groupList">
<li><h:outputText value="#{permission.permissionSetItemId}"/></li>
</a4j:repeat>
</ul>
</h:form>
So, the problem is a4j:repeat is never updated. If I replace the repeat with an outputtext, and set the commandbutton to render it, the page is updated "correctly".
The <a4j:repeat> does not render any HTML to the response. The render attribute of <a4j:ajax> however expects a component with that ID being physically present in the HTML DOM tree (it is namely behind the scenes using JavaScript document.getElementById() stuff to get the element which needs to be updated after the ajax response has returned). This is thus not possible in combination with <a4j:repeat>. You would need to specify its parentmost JSF HTML component instead, or at least to wrap it in a new one.
E.g.
<a4j:commandButton action="#{assetUploadForm.addGroupRights}" value="add group" render="groupList"/>
<h:panelGroup id="groupList">
<ul>
<a4j:repeat value="#{assetUploadForm.groupPermissions}" var="permission">
<li><h:outputText value="#{permission.permissionSetItemId}"/></li>
</a4j:repeat>
</ul>
</h:panelGroup>
As a different alternative, you can also just use the <rich:list> component instead which renders the complete <ul><li> for you already. You're then basically updating the <ul> directly.
<a4j:commandButton action="#{assetUploadForm.addGroupRights}" value="add group" render="groupList"/>
<rich:list id="groupList">
<h:outputText value="#{permission.permissionSetItemId}"/>
</rich:list>
To learn about which components all are available and how to use them, peek around in the showcase site and the component reference.
I don't know why it does not work but as a work-around you could probably use h:dataTable for this. With some css tweaking that could be rendered with the same looks.
Or wrap a a4j:outputPanel around the a4j:repeat and try what happens if you render that.
MAG,
Milo van der Zee
Related
I'm populating <p:selectOneMenu> from a database which contains a list of zones, when a JSF page loaded.
When a zone in this menu is selected, a set of <p:inputText> is displayed in which a user can insert charge that corresponds to product weight which is to be transferred by a transporter to the selected zone in the menu. This can be shown in the following snap shot.
As can be seen, when non numeric values are entered by a user, validation violations occurs, when the given save button <p:commandButton> is pressed (the numbers displayed on top of each text field correspond to weight).
If a user now change the zone in the menu - the first panel without pressing the reset button, the data corresponds to that newly selected zone is loaded in these text fields only when the reset button is pressed as follows (because of validation violation)..
So, how to load data after previous validation violation, if an item (zone) is changed in the menu?
The change event of <p:selectOneMenu>, in this case should do the function something like which is done by <p:resetInput>.
Hope you will be able to understand what I mean :).
Basically, you need the functionality provided by <p:resetInput> inside <p:ajax> of a <p:selectOneMenu>. This is indeed not possible as <p:resetInput> requires being placed in a component implementing ActionSource such as UICommand components.
Your best bet is to let <p:remoteCommand> take over the <p:ajax> change listener job. Therein you can put a <p:resetInput>.
Imagine that you currently have a:
<h:form>
<p:selectOneMenu id="zone">
<f:selectItems ... />
<p:ajax listener="#{bean.changeZone}" update="data" />
</p:selectOneMenu>
<p:panel id="data">
...
</p:panel>
</h:form>
Then this change should do:
<h:form>
<p:selectOneMenu id="zone" onchange="changeZone()">
<f:selectItems ... />
</p:selectOneMenu>
<p:remoteCommand name="changeZone" process="#this zone" action="#{bean.changeZone}" update="data">
<p:resetInput target="data" />
</p:remoteCommand>
<p:panel id="data">
...
</p:panel>
</h:form>
Don't forget to remove the AjaxBehaviorEvent argument from the listener method. It's useless in this particular case anyway.
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
want to create textbox on the fly is it possible?
Select Report to run:
<h:selectOneMenu value="#{reportBean.selectReport}">
<f:selectItems value = "#{reportBean.allReports}" />
<f:ajax listener="#{reportBean.getReqID}" render="reqID"> </f:ajax>
</h:selectOneMenu>
Seems like you want to show/hide the <h:inputText> based on the selected value on your <h:selectOneMenu>. Yes, this can be easily achieved with plain JSF.
Note that if you use set the rendered attribute as false the component won't appear in the component tree, so there will be no way it can't be referenced for any call (not even ajax calls). In order to update it, you should wrap the component inside another component like <h:panelGroup> and render the wrapper. Basic example:
<h:form id="frmRep">
<h:selectOneMenu value="#{reportBean.selectReport}">
<f:selectItems value = "#{reportBean.allReports}" />
<!--
assuming your reportBean.getReqID method will change the value of
reportBean.showReqID attribute to render/not render it and works well
-->
<f:ajax listener="#{reportBean.getReqID}" render="pnlRepName" />
</h:selectOneMenu>
<h:panelGroup id="pnlRepName">
<h:inputText id="reqID" rendered="#{reportBean.showReqID}"
value="#{reportBean.reportName}" />
<h:panelGroup>
</h:form>
For this specific requirement instead, I won't recommend using an ajax call since it has to go to the server to only check if the component should or should not be showed to the user. I would opt for a JavaScript solution to handle this just on client side.
i have two radio buttons and no one is selected by default, and it's mandatory to select one of them, so here's what i did:
<div id="payment_method">
<h:message for="sel_payment_method" style="Color:red;"/>
<h:selectOneRadio id="sel_payment_method" required="true" requiredMessage="Please select a payment method" value="#{myBean.selectedPaymentMethod}">
<f:selectItem itemLabel="Debit or Credit Card" itemValue="credit" />
<f:selectItem itemLabel="Checking Account" itemValue="checking" />
<f:ajax event="change" render="credit_inputs_fragment checking_inputs_fragements" />
</h:selectOneRadio>
</div>
the selectedPaymentMethod property is a string and its default value is null
and the credit_inputs_fragment is a ui:fragement that contains a div
ISSUE: when clicking on a radio buttons the two fragments to be changed are not affected.
please advise, thanks.
You're not entirely clear in describing the concrete problem, but I can see at least two potential problems in the code posted so far:
The <f:ajax event="change"> is wrong in case of radiobuttons and checkboxes. It should be event="click". Or, better, just remove it altogether. It defaults to the right one already. See also What values can I pass to the event attribute of the f:ajax tag?
The component referenced in <f:ajax render> must be a fullworthy JSF UI component and always be rendered to the client side. If the component is by itself never rendered to the HTML, then JavaScript simply can't find it to update its content. You basically need the conditionally rendered components in another component which is always rendered. See also Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
i followed the example code in the following link, and it solved my issue:
JSF: Dynamically change form
I'm developping an application in JSF2.0. I encountered a problem when mixing ajax and a panelGroup with a rendered attribute.
My page contains 3 panels. In the first panel the user selects an item from a <h:selectOneMenu>. This causes to render the second panel.
The second panel contains an h:dataTable that is connected to a DataModel in the backing bean. The values of the dataTable are determined by the item that was chosen in the <h:selectOneMenu> of the first panel. Each row in the table contains a commandLink to view detailed info about the item of that row:
<h:commandLink action="#{faseController.selectInvulling}">
<f:ajax render="#form" execute="#form" />
</h:commandLink>
Clicking on the commandLink the third panel gets rendered:
<h:panelGroup rendered="#{faseController.invullingSelected}" layout="block" styleClass="panelElement" id="invulling">
<label>
<span>Datum:</span>
<h:inputText value="#{faseController.selectedInvulling.datum}" required="true" requiredMessage="Gelieve een datum in te vullen." converterMessage="Gelieve een geldige datum in te vullen (dd/mm/jjjj)." >
<f:convertDateTime pattern="dd/MM/yyyy" />
</h:inputText>
</label>
<label>
<span>Evaluatie</span>
<h:inputTextarea value="#{faseController.selectedInvulling.evaluatie}" cols="100" />
</label>
<label>
<span>Methode</span>
<h:inputTextarea value="#{faseController.selectedInvulling.methode}" cols="100" />
</label>
</h:panelGroup>
This is where I encounter a problem. The first time I click on a commandLink to view the detailed info about that row in the table, I get the correct data in the third panel. Afterwards I change the item in the <h:selectOneMenu> of the first panel: the correct corresponding dataTable gets rendered in the second panel. However this time when I click on a commandLink in the table to view the details of that item, I get to see the info from the item I clicked the first time.
If I ommit rendered="#{faseController.invullingSelected}" everything works fine, but this causes confusion in the view, since the third panel is now always rendered.
All three panels are located in the same form. My backing bean is ViewScoped. What am I doing wrong? Any help woud be greatly appreciated.
This can happen if you're doing business logic in an getter method instead of in an (action)listener method. I also wonder if you really need to execute and render the entire form. Try to be more specific in specifying the elements which are to be executed and/or re-rendered.
The question is way too broad and the code is not complete enough in order to pinpoint the real cause and propose the a ready-to-use solution.
Try using this.
<a4j:commandLink action="#{faseController.selectInvulling}" execute="#this"
render="thirdPanelId" limitRender="true"/>
Execute only the components which you want to process and render only the components which needs to be refreshed.