Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validate readonly components anyway on form submit

We have a UI for an entity that either has an instrument or an issuer, but never both. You can select one of each from a popup search dialog and assign it to the entity.

The user can see the number and name of each of the two next to the "Search..." button. Both of these must be non-editable, that is we the the respective <p:inputText ... readonly="true" ... /> to grey out the inputs.

The requirement we have now is that not only a validation message is supposed to be shown when either none or both of the instrument and issuer are selected, but also that the text boxes tagged with readonly="true" are shown in red (along with their labels).

When adding, before pressing "Save":

enter image description here

When adding, after pressing "Save":

enter image description here

I tried a few things and then I ended up with OmniFaces <o:validateMultiple>, which seemed to go into the right direction. However, I then noticed that the validator didn't kick in, as you can see from the images... (I just removed the readonly="true" for you to see the effect)

Here's the code excerpt:

<!-- this panel is insider a form, yes -->
<h:panelGroup id="subpanel"
              layout="block">
    <o:validateMultiple id="instrument-or-issuer-validator"
                        validator="#{barrierCrossingManager.validateInstrumentOrIssuer}"
                        components="instrument-isin instrument-description issuer-number issuer-name"
                        message="..." />
    <!-- some panel grid with the inputs -->
    ...
        <p:panelGrid id="issuer-subpanel">
            <p:row>
                <p:column>
                    <p:outputLabel for="issuer-number" value="#{msg['common.number.label']}:" />
                </p:column>
                <p:column colspan="3">
                    <p:inputText id="issuer-number"
                                 value="#{barrierCrossingManager.selectedIssuer.nbr}"
                                 readonly="false" />
                </p:column>
                ...
            </p:row>
        </p:panelGrid>
    ...
    <h:messages id="msgs"
                layout="list"
                styleClass="subcontent-validation-msg-wrapper"
                showDetail="false"
                showSummary="true" />
</h:panelGroup>

Q:

How do you solve this?

The problem seems to be that OmniFaces or JSF(?) doesn't do validation of readonly components. I also looked at the OmniFaces VDL for <o:validateMultiple>, but there wasn't a flag like allowReadOnlyComponentHighlighting or similar.

Is there another way?

like image 552
Kawu Avatar asked Jun 06 '14 18:06

Kawu


1 Answers

JSF will for security reasons indeed skip readonly (and disabled) fields from processing (conversion, validation and model update). You don't want a hacker to be able to edit those values anyway by manipulating the request parameters, right? OmniFaces will also skip them the same way for obvious security reasons.

Your best bet is putting an EL expression in readonly attribute which evaluates false during apply request values and validations phases (the 2nd and 3rd phase), but true during all other phases. It suffices to check if the current phase ordinal is 4 or greater. This should allow JSF to perform validation on the value anyway. Then, you need to add an EL expression to the rendered attribute which evaluates false during update model values phase (the 4th phase), so that the model value won't be updated because readonly was false during apply request values and validations phases (and thus the component has its local value set, which triggers the model update).

You can obtain the current JSF phase by FacesContext#getCurrentPhaseId() which is in EL also available by #{facesContext.currentPhaseId}. The PhaseId has in turn a getOrdinal() method returning the phase index (from 1 until with 6 for the standard phases).

Summarized, this should do in order to validate them anyway, but prevent model update:

<p:inputText
    readonly="#{facesContext.currentPhaseId.ordinal ge 4}" 
    rendered="#{facesContext.currentPhaseId.ordinal ne 4}" 
/>
like image 119
BalusC Avatar answered Nov 15 '22 04:11

BalusC