I'm building on BalusC's solution to highlight and focus fields in JSF. My plan is to output a JSON array with ids and then have a method be called which will process this array. This works fine when I don't use <f:ajax/>
Here is my phase listener solution:
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
List<String> highlightFields = new ArrayList<String>();
Iterator<String> highlightFieldsItr = facesContext
.getClientIdsWithMessages();
while (highlightFieldsItr.hasNext()) {
StringBuilder sb = new StringBuilder();
sb.append("#");
sb.append(highlightFieldsItr.next().replaceAll(":", "\\\\:"));
highlightFields.add(sb.toString());
}
JSONArray jsonHighlightFields = new JSONArray(highlightFields);
facesContext.getExternalContext().getRequestMap()
.put("errorFields", jsonHighlightFields.toString());
}
Basically this would produce errorFields value with something like ["#some\:id1", "#some\id2"]. Then I can do something like this in my root layout file:
<script>
var errorFields = ${errorFields}; // This will xlate to ["#some\\:id1", "#some\\:id2"
$(document).ready(function(){
processInputErrors(errorFields);
});
</script>
With a processInputErrors function like this:
function processInputErrors(ids) {
for (id in ids) {
if (focus == false) {
jQuery(ids[id]).focus();
focus = true;
}
jQuery(ids[id]).addClass('input-error');
}
}
However, I need to somehow obtain this list in the function which gets called on success of an ajax post.
Now f:ajax does have the onevent attribute and this function does get called, but I'm not sure exactly what it gets passed. How would I be able somehow pass the invalid Ids from the phase listener to this function? It seems to be passed an object which represents the HTMLInputElement?
<f:ajax event="change" onevent="test" render="test test_msg" immediate="true" />
Happy to hear about alternative suggestions or ideas. The goal is basically to focus and highlight the field(s) which are invalid not only on a full post-back but also when using f:ajax.
Thanks!
That article was more targeted on JSF 1.x. JSF 2.x offers now a lot of advantages of which the following are beneficial for your particular requirement:
You can refer the current component in EL by #{component}. In case of input components this is the UIInput which in turn has an isValid() method. This could be used in styleClass attribute.
You can use <f:ajax> to re-render parts of the view, also <script> elements.
1+1 is...
<h:inputText id="input1" value="#{bean.input1}" required="true" styleClass="#{component.valid ? '' : 'error'}">
<f:ajax render="#this input1message focus" />
</h:inputText>
<h:message id="input1message" for="input1" />
...
<h:inputText id="input2" value="#{bean.input2}" required="true" styleClass="#{component.valid ? '' : 'error'}">
<f:ajax render="#this input2message focus" />
</h:inputText>
<h:message id="input2message" for="input2" />
...
<h:panelGroup id="focus"><script>jQuery(":input.error:first").focus();</script></h:panelGroup>
No need for a PhaseListener anymore. You could if necessary wrap the input markup boilerplate in a composite component or a tag file.
Back to your concrete question about the onevent attribute, check this answer: JSF 2: How show different ajax status in same input?
Related
I have a p:commandLink in my xhtml with the value toggling between "Show"/"Hide".
Is there any way by which I can get the value of this commandlink from the backing bean?
I mean, I want to know what value the command link is showing currently i.e. Show/Hide?
To the point, the invoking component is just available in ActionEvent argument:
<h:commandLink id="show" value="Show it" actionListener="#{bean.toggle}" />
<h:commandLink id="hide" value="Hide it" actionListener="#{bean.toggle}" />
with
public void toggle(ActionEvent event) {
UIComponent component = event.getComponent();
String value = (String) component.getAttributes().get("value");
// ...
}
However, this is a poor design. Localizable text should absolutely not be used as part of business logic.
Rather, either hook on component ID:
String id = (String) component.getId();
or pass a method argument (EL 2.2 or JBoss EL required):
<h:commandLink id="show" value="Show it" actionListener="#{bean.toggle(true)}" />
<h:commandLink id="hide" value="Hide it" actionListener="#{bean.toggle(false)}" />
with
public void toggle(boolean show) {
this.show = show;
}
or even just call the setter method directly without the need for an additional action listener method:
<h:commandLink id="show" value="Show it" actionListener="#{bean.setShow(true)}" />
<h:commandLink id="hide" value="Hide it" actionListener="#{bean.setShow(false)}" />
As #BalusC suggested, your approach is not a good solution. But if you really want to do that, you can bind the component (p:commandLink) to your backingbean, as seen in What is the advantages of using binding attribute in JSF?.
After the component was bound, you can access the value attribute from the p:commandLink.
I have a primefaces p:dataTable with InCell editing enabled and want to trigger/activate the RowEditor for the newly added row.
Excerpt of XHTML
<p:commandButton id="btnAddEntry" value="Add new row" actionListener="#{myBean.addNewCar}" ... update="carTable growl" process="#this carTable ..."/>
<p:dataTable id="carTable" var="car" value="#{myBean.cars}" ... editable="true">
<p:column ...>
<p:cellEditor>
...
</p:cellEditor>
</p:column>
...
<p:column ...>
<p:rowEditor />
</p:column>
...
</p:dataTable>
Here is what i have so far for the bean method:
public void addNewCar() {
Car newCar = new Car();
cars.add(newCar);
FacesContext facesContext = FacesContext.getCurrentInstance();
UIComponent uiTable = ComponentUtils.findComponent(facesContext.getViewRoot(), "carTable");
DataTable table = (DataTable) uiTable;
final AjaxBehavior behavior = new AjaxBehavior();
RowEditEvent rowEditEvent = new RowEditEvent(uiTable, behavior, table.getRowData());
rowEditEvent.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
table.broadcast(rowEditEvent);
}
i don't know
if this is the correct approach
in case yes, which object to pass to the constructor RowEditEvent(UIComponent component, Behavior behavior, Object object) as the 3rd parameter
If you have only one data table in the facelet try to use this
oncomplete="jQuery('.ui-datatable-data tr').last().find('span.ui-icon-pencil').each(function(){jQuery(this).click()});
Add this to the command button. This should work.
I used the execute(..) method of class RequestContext in my create method. This allowed me to first calculate the index of the row to go into edit mode and to include it dynamically in the javascript:
RequestContext.getCurrentInstance().execute("jQuery('span.ui-icon-pencil').eq(" + rowToEditIndex + ").each(function(){jQuery(this).click()});");
Hope this helps.
You can add an unique styleClass to the dataTable, and one javascript function in the commandButton:
So add to the table:
styleClass="myTable"
And to the button:
oncomplete="$('.myTable tbody.ui-datatable-data tr:last-child td span.ui-row-editor span.ui-icon-pencil').click()"
And your code will look like this:
<p:commandButton id="btnAddEntry" value="Add new row" actionListener="#{myBean.addNewCar}" ... update="carTable growl" process="#this carTable ..." oncomplete="$('.myTable tbody.ui-datatable-data tr:last-child td span.ui-row-editor span.ui-icon-pencil').click()"/>
<p:dataTable styleClass="myTable" id="carTable" var="car" value="#{myBean.cars}" ... editable="true">
<p:column ...>
<p:cellEditor>
...
</p:cellEditor>
</p:column>
...
<p:column ...>
<p:rowEditor />
</p:column>
...
</p:dataTable>
This solution aims to resolve a few drawbacks of the original answer of:
oncomplete="jQuery('.ui-datatable-data tr').last().find('span.ui-icon-pencil').each(function(){jQuery(this).click()});"
The above statement retrieves all "tr" elements which are descendants (at any level) of elements with the ".ui-datatable-data" class. The potential issues with that are:
As mentioned by #Gnappuraz, if there are multiple p:datatable's in the document, this statement will choose the last table.
Additionally, I ran into a problem where even with one datatable, a column with a p:selectOneRadio component will also render a html table (that contains "tr" elements). In this case the selector chooses the last "tr" for the p:selectOneRadio's table, rather than the p:datatable.
Not a problem, but the statement can be shortened to exclude the each() because of jQuery's implicit iteration.
What I ended up with was:
oncomplete="jQuery('#tableForm\\:table .ui-datatable-data > tr').last().find('span.ui-icon-pencil').click();"
This selector says - get the last "tr" element, which is a direct child of an element with class ".ui-datatable-data", which is also a descendant of the element with id "table" in form "tableForm". In other words you can now have multiple p:datatables, and any include any components that render html tables.
Try this :
jQuery('#FormID\\:DataTableID\\:#{datatable.size()}\\:rowEditorID').find('span.ui-icon-pencil').each(function(){jQuery(this).click()});
worked well, no matter how many datatables in a same page.
If you have several DataTable which each of them has an ID, then try to use one of these solutions:
Oncomplete="jQuery('#FrmID:TabViewI:mydataTableID'.replace(/:/g,'\\:')).find('span.ui-icon-pencil').each(function(){jQuery(this).click()});"
OR:
oncomplete="jQuery('#FrmID\\:TabViewID\\:mydataTableID').find('span.ui-icon-pencil').each(function(){jQuery(this).click()});"
This should work perfectly.
Execute a JQuery script on complete process :
oncomplete="editNewRow()"
<script type="text/javascript">
$(document).on("keydown", ".ui-cell-editor-input input", function(event) {
if (event.keyCode == 13) {
$(this).closest("tr").find(".td-edition .ui-row-editor .ui-row-editor-check .ui-icon-check").click();
return false;
}
});
function editNewRow() {
$("#pim\\:invoiceRuleCretariaTable tbody:first tr:first .td-edition .ui-row-editor .ui-icon-pencil").click();
}
</script>
The first function submit the addition action by press on "Enter" key
Execute a JQuery script on complete process :
oncomplete="editNewRow()"
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).on("keydown", ".ui-cell-editor-input input", function(event) {
if (event.keyCode == 13) {
$(this).closest("tr").find(".td-edition .ui-row-editor .ui-row-editor-check .ui-icon-check").click();
return false;
}
});
function editNewRow() {
$("#panel\\:carTable tbody:first tr:last .td-edition .ui-row-editor .ui-icon-pencil").click();
}
</script>
The first function submit the addition action by press on "Enter" key
To solve this is better to use styleClass and because for fix this it is necessary to use javascript, you need to check the html tags in your browser, because it seems that they change with the versions of primefaces. For vesion 6.1 I've put this on my bean:
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.execute("$('.styleEventosPlanPrestacion tbody.ui-datatable-data tr:first-child td div.ui-row-editor a.ui-row-editor-pencil').click()");
and In my .xhtml I've put this:
<p:dataTable ... styleClass="styleEventosPlanPrestacion">
Ok I have a simple many menu in wich I call a listener
<p:selectManyMenu style="width: 100%;" id="cmbsectores" valueChangeListener="#{mbcompletado.removeItem}">
<f:selectItems value="#{mbcompletado.sectores}"/>
<f:ajax update="#this"/>
</p:selectManyMenu>
I am looking the way I can use the ValueChangeEvent pass as parameter to detect which item was selected??
So I can use my business logic!
Do i need to use ajax tag? I found an itemSelect event in primeface, framework which I am using, but it only works on charts components!
Thanks in advance
Since you are already using PrimeFaces use p:ajax instead of f:ajax. The event is already set to the appropriate event (valueChanged).
To detect the selected values of the selectManyMenu the value attribute is necessary:
<p:selectManyMenu style="width: 100%;" id="cmbsectores"
value="#{mbcompletado.selectedSectores}">
<f:selectItems value="#{mbcompletado.sectores}"/>
<p:ajax/>
</p:selectManyMenu>
You can remove the valueChangeListener listener altogether.
For a more complete example see SelectManyMenu.
EDIT:
In your backing bean mbcompletado.selectedSectores should point to a collection of the same type like your mbcompletado.sectores. For example, if your sectores is a List of TypeA, selectedSectores should be also a List of the same type (TypeA).
Similar backing-bean structure can be found in the following example SelectManyCheckbox.
You need the <f:ajax listener> (or in this case better <p:ajax listener>) method instead. The ValueChangeListener serves an entirely different purpose and should only be used when you're really interested in both the old and new value, not when you're only interested in the new value.
E.g.
<p:selectManyMenu value="#{bean.selectedSectors>
<f:selectItems value="#{bean.availableSectors}"/>
<p:ajax listener="#{bean.selectedSectorsChanged}" />
</p:selectManyMenu>
with
private List<String> selectedSectors;
private List<String> availableSectors;
public void selectedSectorsChanged() {
System.out.println("Selected sectors are: " + selectedSectors); // Look, JSF has already set it.
// ...
}
See also:
When to use valueChangeListener or f:ajax listener?
I have a form inside a dialog which I close by clicking on commandbutton with ajax ,
like this
<h:commandButton value="Add" action="#{myBean.addSomething(false)}"
id="add_something_id" >
<f:ajax render="#form someTable" execute="#form"
onevent="closeAddNewSomethingDialogIfSucceeded"></f:ajax>
</h:commandButton>
and here is the js code for closing the dialog
function closeAddNewSomethingDialogIfSucceeded(data) {
if(data.status === 'success') {
$("#dialog_id").dialog("close");
}
}
No problems till here...
Now I changed some of the dialog form fields into required="true" and now I want to prevent the closing of the dialog of i got validation errors...
But the ajax data.status still reaches its success state , and I can't figure out what indication of validation failure I can hook on...
any ideas?
Thanks to BalusC answer I did the following:
in JSF , added :
<h:panelGroup id="global_flag_validation_failed_render">
<h:outputText id="global_flag_validation_failed" value="true"
rendered="#{facesContext.validationFailed}"/>
</h:panelGroup>
the f:ajax was changed into
<f:ajax render="#form someTable global_flag_validation_failed_render"
and in js added the following check
if(data.status === 'success') {
if($("#global_flag_validation_failed").length === 0){
$("#dialog_id").dialog("close");
}
}
Not specifically for required="true", but you can check by #{facesContext.validationFailed} if validation has failed in general. If you combine this with checking if the button in question is pressed by #{not empty param[buttonClientId]}, then you can put it together in the rendered attribute of the <h:outputScript> as follows:
<h:commandButton id="add_something_id" binding="#{add}" value="Add" action="#{myBean.addSomething(false)}">
<f:ajax execute="#form" render="#form someTable" />
</h:commandButton>
<h:outputScript rendered="#{not empty param[add.clientId] and not facesContext.validationFailed}">
$("#dialog_id").dialog("close");
</h:outputScript>
(note that you need to make sure that the script is also re-rendered by f:ajax)
A bit hacky, but it's not possible to handle it in the onevent function as the standard JSF implementation doesn't provide any information about the validation status in the ajax response.
If you happen to use RichFaces, then you could just use EL in the oncomplete attribute of the <a4j:xxx> command button/link. They are namely evaluated on a per-request basis instead of on a per-view basis as in standard JSF and PrimeFaces:
<a4j:commandButton ... oncomplete="if (#{!facesContext.validationFailed}) $('#dialog_id').dialog('close')" />
Or if you happen to use PrimeFaces, then you could take advantage of the fact that PrimeFaces extends the ajax response with an additional args.validationFailed attribute which is injected straight in the JavaScript scope of the oncomplete attribute:
<p:commandButton ... oncomplete="if (args && !args.validationFailed) $('#dialog_id').dialog('close')" />
(note that & is been used instead of &, because & is a special character in XML/XHTML)
Or you could use the PrimeFaces' RequestContext API in the bean's action method to programmatically execute JavaScript in the rendered view.
RequestContext.getCurrentInstance().execute("$('#dialog_id').dialog('close')");
No conditional checks are necessary as the bean's action method won't be invoked anyway when the validation has failed.
Two things
1) Checking for an error in the 'onevent' function
Surely you have a message tag for the mandatory field?
<h:message id="m-my-field-id" for="my-field-id" errorClass="error-class"/>
So you can check for the error-class something like
var message = $('#m-my-field-id');
if(message.hasClass('error-class'){
//do this
}
else{
//do that
}
2) The DOM isn't up to date on success
Yes, I can see the message on the page in Firefox, yet jQuery tells me it is not there.
I have found that using the smallest possible timeout is sufficient to fix this
setTimeout(
function(){
setErrorStyle(source.attr('id'));
},
1
);
I think you should take a look at PrimeFaces' RequestContext. This would help you trigger client-side code on the server side.
#BalusC
in your example code the clientId of the button is not set as a param because it is a AJAX request. So
not empty param[add.clientId] is always false.
But this works:
param['javax.faces.source'] eq add.clientId
(tested with jsf-impl-2.2.12.redhat-1)
regards
I'm coming with a question on JSF2. I'm currently mixing two different pieces of code proposed by BalusC on his blog :
http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html
http://balusc.blogspot.com/2007/12/validator-for-multiple-fields.html
The first let highlight in red the fields that do have an error message.
The second let validation be performed on multiple fields.
I'm looking for a way to have a single error message (dont want the message to be rendered twice) in the FacesContext be linked to several client ids (because the message do concern several fields due to the multiple fields validator).
Is it possible with the language basics ?
If I can, I'd like to avoid a hand-made system (it should work with a managed bean with "request" scope, the validator placing the clientIds that do have an error into a List accessed by the PhaseListener).
Thanks in advance for your tips. Could not see anything close to addMessage() on FacesContext that could do the work, but maybe there is a way...
If the message is appearing twice, then it means that you're either firing the same validator by both components or that the validator is fired once but implicitly adding the message to the other component.
I understand that you want to mark the both components as invalid (so that they get highlighted) and that you want only one message. In that case, you need to make sure that the validator is fired once and that the other component is retrieving an empty/null message.
You only need to change the validator to retrieve the whole component as attribute instead of its value (note: I've in the meanwhile edited the old article accordingly; it has another benefits) and you need to change the phase listener to remove empty/null messages.
E.g. in view:
<h:outputLabel for="password" value="Password" />
<h:inputSecret id="password" value="#{bean.password}" required="true">
<f:validator validatorId="passwordValidator" />
<f:attribute name="confirm" value="#{confirm}" />
</h:inputSecret>
<h:message for="password" styleClass="error" />
<h:outputLabel for="confirm" value="Confirm password" />
<h:inputSecret id="confirm" binding="#{confirm}" required="true" />
<h:message for="confirm" styleClass="error" />
and in validate() method:
String password = (String) value;
UIInput confirmComponent = (UIInput) component.getAttributes().get("confirm");
String confirm = confirmComponent.getSubmittedValue();
if (password == null || password.isEmpty() || confirm == null || confirm.isEmpty()) {
return; // Let required="true" do its job.
}
if (!password.equals(confirm)) {
confirmComponent.setValid(false);
context.addMessage(confirmComponent.getClientId(context), new FacesMessage(null));
throw new ValidatorException(new FacesMessage("Passwords are not equal."));
}
and in the phase listener:
Iterator<String> clientIdsWithMessages = facesContext.getClientIdsWithMessages();
while (clientIdsWithMessages.hasNext()) {
String clientIdWithMessages = clientIdsWithMessages.next();
if (focus == null) {
focus = clientIdWithMessages;
}
highlight.append(clientIdWithMessages);
if (clientIdsWithMessages.hasNext()) {
highlight.append(",");
}
Iterator<FacesMessage> messages = facesContext.getMessages(clientIdWithMessages);
while (messages.hasNext()) {
if (messages.next().getSummary() == null) {
messages.remove(); // Remove empty messages.
}
}
}
Related:
JSF doesn't support cross-field validation, is there a workaround?
How validate two password fields by ajax?
Unrelated to the concrete problem, there's in JSF2 by the way an other way to highlight invalid fields. You can do it by the new implicit #{component} variable in EL:
<h:inputText styleClass="#{component.valid ? '' : 'error'}" />