How to conditionally invoke a4j:commandButton oncomplete script depending on action outcome - jsf

I have a
<a4j:commandButton action="#{myBean.doCalculation}"
oncomplete="redirectBrowser()" />
However, if there is an error at myBean.doCalculation(), then oncomplete attribute is always invoked. How do I let it depend on result of action method?

You can check for errors in the oncomplete event and redirect when none are found. It can be done like this
oncomplete="#{facesContext.maximumSeverity == null ? 'redirectBrowser()' : 'doSomethingElse()'}"

If you're on JSF 2.0, you can use FacesContext#isValidationFalied() to check if validation has failed or not:
oncomplete="if (#{not facesContext.validationFailed}) redirectBrowser()"
However, just performing redirect in the action method is more clean:
public String doCalculation() {
// ...
if (success) {
return "nextPage.xhtml?faces-redirect=true";
} else {
return null;
}
}
Yet more clean would be to just do the validation by a Validator. This way the action method will simply not be invoked at all when the validation has failed.

Well, just looking for facesContext.maximumSeverity == null is not enough, there may be a warning as current max severity, that should redirect, too. Instead, write a method that looks if there are any max severities of priority error...

Related

Should I put this code in the shouldUpdate call?

I have the following shouldUpdate call
shouldUpdate(changedProperties: PropertyValues): boolean {
if (changedProperties.has("mobile") &&
!this._isOpeningDialog &&
this._testStore.isShareSheetOpen) {
this._testStore.closeShareSheet();
}
return this._isOpeningDialog;
}
I need to check if the mobile property has changed and if so update the element. However, this seems like an anti-pattern. Does anyone have a better way to do this?
this._testStore.closeShareSheet() causes the element to re-render and shouldUpdate to be called.

Programatically Set Attribute with a Value Expression when component is within JSTL forEach loop

First time asking a question so please bear with me.
I have a pure Java custom component that extends UIInput (JSF 2.2, Mojarra) and I am using it like so:
<c:forEach items="#{bean.items}" var="item">
<my:component item="#{item}" />
</c:forEach>
I am trying to avoid unnecessarily specifying the 'value', 'valueChangeListener' and 'validator' attributes on the tag in the .xhtml file.
In my custom component I have overridden the setValueExpression method like so:
#Override
public void setValueExpression(String name, ValueExpression expression) {
super.setValueExpression(name, expression);
if ("item".equals(name)) {
this.setValue(Components.createValueExpression("#{item.myValue}", MyValue.class));
this.addValueChangeListener(new MethodExpressionValueChangeListener(Components.createVoidMethodExpression("#{item.myValueChanged}", ValueChangeEvent.class)));
this.addValidator(new MethodExpressionValidator(Components.createVoidMethodExpression("#{item.validateMyValue}", FacesContext.class, UIComponent.class, Object.class)));
}
}
I'm using OmniFaces there with its Components utility to reduce boilerplate code.
When it comes time to act on any of those three (validate on submit, for example) it results in:
javax.faces.FacesException: javax.el.PropertyNotFoundException: Target Unreachable, identifier 'item' resolved to null
I am pretty sure I know why, I just don't know what to do about it.
I believe when it comes time for the three expressions I am trying to set programatically to be resolved it's trying to find a bean in some scope by the name, 'item' however that doesn't exist since 'item' was a point in time variable within a JSTL forEach loop.
I think there's a special deferred kind of expression that Weld is using for item itself (which I can kinda see when I debug that setValueExpression method) that is aware or otherwise has references to that point in time variable but I'm not doing the same thing when I set up those three expressions and therefore there's no handle to that later on when it comes time for them to be resolved.
I am sure there is a way to wire this together I'm just not seeing it.
Additionally, I know I could just put the three attributes on the tag in the .xhtml like this:
<my:component item="#{item}" value="#{item.myValue}" valueChangeListener="#{item.myValueChanged}" validator="#{item.validateMyValue}" />
Then they would get their special deferred expressions just like item itself does (and indeed everything works as expected this way) but I'd rather not - it's something I'd have to repeat a lot and it just seems like there should be a way to do what I am trying above.
I believe I found the answer.
What I kept seeing in the debugger kept nagging at me, I just wasn't sure how to wire up the same scenario manually, then I found a Stack Overflow post with the following code snip at the very bottom:
VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable(mappingName, component.getValueExpression(mappedAttributeName));
return new ValueExpressionImpl(expression, null, null, varMapper, expectedType);
That was enough to point me in the right direction of re-using the incoming value expression for item itself in a VariableMapper instance which is in turn used to create the three value/method expressions so they each now have a handle & can resolve 'item' later on, when it comes time:
#Override
public void setValueExpression(String name, ValueExpression expression) {
super.setValueExpression(name, expression);
if ("item".equals(name)) {
VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable("item", expression);
ValueExpressionImpl valExprImpl = new ValueExpressionImpl("#{item.myValue}", null, null, varMapper, MyValue.class);
super.setValueExpression("value", valExprImpl);
MethodExpressionImpl meExprImpl = new MethodExpressionImpl("#{item.myValueChanged}", null, null, varMapper, Void.class, new Class<?>[] {ValueChangeEvent.class});
MethodExpressionValueChangeListener mevcl = new MethodExpressionValueChangeListener(meExprImpl);
this.addValueChangeListener(mevcl);
meExprImpl = new MethodExpressionImpl("#{item.validateMyValue}", null, null, varMapper, Void.class, new Class<?>[] {FacesContext.class, UIComponent.class, Object.class});
MethodExpressionValidator mev = new MethodExpressionValidator(meExprImpl);
this.addValidator(mev);
}
}
That seems to have done the trick (and looks so simple now...).

How do I get a parameter to not just display in a component, but also be recognized inside of OnInitializedAsync()?

I'm working on a blazor server-side project and I have a component that gets passed a model (pickedWeek) as a parameter. I can use the model fine in-line with the html, but OnInitializedAsync always thinks that the model is null.
I have passed native types in as parameters, from the Page into a component, this way without an issue. I use a NullWeek as a default parameter, so the number getting used in OnInitializedAsync only ever appears to be from the NullWeek. In case this is related, there is a sibling component that is returning the Week model to the Page through an .InvokeAsync call, where StateHasChanged() is being called after the update. It appears that the new Week is getting updated on the problem component, but that OnInitializeAsync() either doesn't see it, or just never fires again- which maybe is my problem, but I didn't think it worked that way.
For instance, the below code will always show "FAILURE" but it will show the correct Week.Number. Code below:
<div>#pickedWeek.Number</div>
#if(dataFromService != null)
{
<div>SUCCESS</div>
}
else
{
<div>FAILURE</div>
}
#code{
[Parameter]
public Week pickedWeek { get; set; }
protected IEnumerable<AnotherModel> dataFromService { get; set; }
protected override async Task OnInitializedAsync()
{
if (pickedWeek.Number > 0)
{
dataFromService = await _injectedService.MakeACall(pickedWeek.Id);
}
}
}
#robsta has this correct in the comments, you can use OnParametersSet for this. Then, you will run into another issue, in that each rerender will set your parameters again and generate another call to your service. I've gotten around this by using a flag field along with the the OnParametersSet method. Give this a shot and report back.
private bool firstRender = true;
protected override async Task OnParametersSetAsync()
{
if (pickedWeek.Number > 0 && firstRender)
{
dataFromService = await _injectedService.MakeACall(pickedWeek.Id);
firstRender = false;
// MAYBE call this if it doesn't work without
StateHasChanged();
}
}
Another alternative is to use the OnAfterRender override, which supplies a firstRender bool in the the method signature, and you can do similar logic. I tend to prefer the first way though, as this second way allows it to render, THEN sets the value of your list, THEN causes another rerender, which seems like more chatter than is needed to me. However if your task is long running, use this second version and build up a loading message to display while the list is null, and another to display if the service call fails. "FAILURE" is a bit misleading as you have it as it's being displayed before the call completes.
I've also found that a call to await Task.Delay(1); placed before your service call can be useful in that it breaks the UI thread loose from the service call awaiter and allows your app to render in a loading state until the data comes back.

PF Calendar in Dialog nulls Java Property in UiComponent#visitTree

Prerequisites:
xmlns:p="http://primefaces.org/ui"
javax.faces.component.UIComponent
Setup:
org.primefaces:primefaces:jar:6.1.2
org.glassfish:javax.faces:jar:2.2.14
Tomcat 8.5.28
Proprietary OR Mapper
#Property
private Calendar cal;
public Calendar getCal() {
return ORMapper.getProperty(this, "cal");
}
Functional setup:
p:commandButton that during its action executes code (that leads to the error) and then opens a dialogue which contains
<p:calendar id="cal" value="#{bean.model.cal}" />
Execution:
During Invoke Application of the commandButton (action) we traverse a section of the DOM tree that does NOT contain the dialogue (i.e. that dialogue is not a child of component) using
component.visitTree(
VisitContext.createVisitContext(
FacesContext.getCurrentInstance(),
null,
Collections.singleton(VisitHint.SKIP_UNRENDERED)),
(context, component) -> {
...
return VisitResult.ACCEPT;
}
);
Before the tree execution
model.cal = value
model.getCal() = value
Errorneous behaviour:
After the tree execution
model.cal = null
model.getCal() = value
This then leads to an error when validating the model which has a violation on e.g. #NotNull on the property (not the getter)
Notes:
Using IntelliJ attempts where made to find the change: Field Access/Modification breakpoints, calls to get/setter breakpoints, EL Method Invoker breakpoints
Removing the visitTree call fixes the Problem
Removing the element from the dialogue fixes the problem
Later on the value is set again, when couldnt be figured out either

jquery Validation Engine funcCall not working if only rule

I have an input field that I am trying to add custom validation to (required depending on another field). If I put required AND funcCall() I can see that two errors are returned. If I only put the funcCall nothing is returned. I know it's getting in the function and the condition because I did a console.log() but for some reason it seems like it needs an initial rule to fail to show the error.
Call:
<input type="text" class="validate[funcCall[validatePassportRequired]]" id="form_register_passport_number" value="" name="passport_number" size="50">
Function:
function validatePassportRequired(field, rules, i, options) {
if ($('#register_for').val()!='Local') {
return options.allrules.required.alertText;
}
}
So If I change the Call to:
class="validate[required, funcCall[validatePassportRequired]]"
I get two * This field is required
Do I have to have another validation rule along with the funcCall?
just add the following line before returning the error message and instead of required in returning message put the function name before .alertText.
rules.push('required');
#sunzyflower in your case your function would see like this..
function validatePassportRequired(field, rules, i, options) {
if ($('#register_for').val()!='Local') {
rules.push('required');
return options.allrules.validatePassportRequired.alertText;
}
}
Use
funcCallRequired[validatePassportRequired]
instead of
funcCall[validatePassportRequired]
This will add required internally without having a double message.
If you want more information about the (old) issue :
https://github.com/posabsolute/jQuery-Validation-Engine/issues/392
https://github.com/posabsolute/jQuery-Validation-Engine/pull/785

Resources