Not A Valid Method Expression in JSF - jsf

I have a commandbutton that I want to use to navigate to another jsf facelet, but I am getting a not a valid method expression:
<h:commandButton value="Save Edits" action="editOrDeletePage.xhtml?editing=true;#{product.id};name=#{product.productName};description=#{product.description};quantity=#{product.quantity}"/>
I can get it to work if I only have
action="editOrDeletePage.xhtml?editing=true"
I guess when I have multiple properties that I'm passing, I am not delimiting them correctly. Any ideas?

When the action attribute contains an EL expression, it's interpreted as a method expression. It's thus really only valid when you use action="#{bean.someMethod}". However, your attempt does not represent a valid method expression, it's represents instead a value expression which is not accepted by the action attribute.
If you intend to append additional request/view parameters to the form submit, then you should rather use <f:param>.
<h:commandButton value="Save Edits" action="editOrDeletePage.xhtml">
<f:param name="editing" value="true" />
<f:param name="id" value="#{product.id}" />
<f:param name="name" value="#{product.productName}" />
<f:param name="description" value="#{product.description}" />
<f:param name="quantity" value="#{product.quantity}" />
</h:commandButton>
Note that those parameters don't end up in the request URL (as you see in the browser address bar) and that your theoretical approach also wouldn't have done that, a JSF command button namely generates a HTML <input type="submit"> element which submits to the very same URL as specified in the action attribute of the parent HTML <form method="post">.
Also note that those parameters are not evaluated during the form submit, but during displaying the form. If you intented to pass submitted values along that way, then you're basically doing it wrong. Perhaps you want to specify them as view parameters so that you can use action="editOrDeletePage?faces-redirect=true&includeViewParams=true" as action.
After all, it's hard to propose the right solution for you as you didn't elaborate the concrete functional requirement in detail at all.

If you are using JSF2 you can use h:button for this
<h:button value="press here" outcome="editOrDeletePage">
<f:param name="productId" value="#{product.id}" />
</h:button>

Related

Passing "get" parameters doesn't work, parameter not visible in the link

I'm a beginner to JSF and I want to code a little searchbar on my future website.
I made two pages : index.xhtml and search.xhtml, and I try to pass get parameters from index.xhtml to search.xhtml, so I made this little formular :
<!-- index.xhtml -->
<h:form id="Form_search">
<h:inputText class="search_bar_text" binding="#{se}"></h:inputText>
<h:button class="search_bar_button" outcome="search">
<f:param name="search" value="#{se.value}" />
</h:button>
</h:form>
To summarize, I want to send the content of an inputText to search.xhtml
But there's a problem : when I click on the submit button, no parameters are passed, so instead of having /search.xhtml?search=foobar I only have /search.xhtml.
I also tried this, but this doesn't work either :
<!-- index.xhtml -->
<h:form id="Form_search">
<h:inputText class="search_bar_text" binding="#{se}"></h:inputText>
<h:button class="search_bar_button" outcome="search.xhtml?search=#{se.value}">
</h:button>
</h:form>
Can someone explain to me the reason of this problem and how I can fix it?
The <f:param value> and <h:button outcome> are evaluated during rendering the HTML output, not during "submitting" of the form as you seem to expect. Do note that there's actually no means of a form submit here. If you're capable of reading HTML code, you should see it in the JSF-generated HTML output which you can see via rightclick, View Source in webbrowser.
Fix it to be a true GET form. You don't need a <h:form>, <h:inputText>, nor <h:button> here at all. You don't want a POST form. You don't seem to want to bind the input to a bean property. You don't want a plain navigation button.
<form id="form_search" action="search.xhtml">
<input name="search" class="search_bar_text" />
<input type="submit" class="search_bar_button" />
</form>
Yes, you can just use plain HTML in JSF.
If you really, really need to use JSF components for this purpose for some reason, then you could also use this POST-redirect-GET-with-view-params trick.
First add this to both index.xhtml and search.xhtml:
<f:metadata>
<f:viewParam name="search" value="#{bean.search}" />
</f:metadata>
Then use this form:
<h:form id="form_search">
<h:inputText value="#{bean.search}" styleClass="search_bar_text" />
<h:commandButton styleClass="search_bar_button" action="search?faces-redirect=true&includeViewParams=true" />
</h:form>
This would perhaps make sense if you intend to use JSF validation on it. But even then, this doesn't prevent endusers from manually opening the URL with invalid params. You'd then better add validation to <f:viewParam> itself on search.xhtml.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for? (scroll to bottom of answer)
How do I process GET query string URL parameters in backing bean on page load?

Button as link, <h:button outcome /> not working (no navigation cases used)

I am trying to achieve the following, though with a button.
<h:outputLink value="/admin/category/read">
Cancel
<f:param name="cat" value="" />
<f:param name="subcat" value="" />
</h:outputLink>
I have tried using h:button, though the outcome property does not work since /admin/category/read is not a specified navigation-case.
How to use a button as link, without having to use a navigation-case or server side method?
No, there's no solution using JSF attributes, at least not if you really don't have a navigation case for the h:button.
If possible, I'd advise to use CSS styling as already mentioned in the comments.
But h:button just creates a plain HTML link with onclick="window.location.href=URL". So if you really want, you can build the URL yourself and just use a plain HTML input button like this:
<input type="button" value="Cancel"
onclick="window.location.href='/admin/category/read?cat=&subcat='; return false;" />
Related:
Difference between h:button and h:commandButton
When should I use h:outputLink instead of h:commandLink?

Cancel button doesn't work in case of validation error

i have a form with some inputs that have validation (required=true)
and when i click on the cancel button in case of validation error, the cancel button doesn't navigate to previous page, instead it removes the validation error (i think it goes back one step that was before the validation error ?)
here's my code:
<h:form>
<h:inputText value="#{myBean.nickName}" id="nickname" required="true"
requiredMessage="nickname should be specified" />
<h:commandLink immediate="true" id="cancel_link" onclick="history.back(); return false" style="float: left;margin: 118px 189px 0 0;">
<h:graphicImage width="90" height="28" value="#{resource['images:blu_btnCancel.png']}" />
</h:commandLink>
</h:form>
please advise how to fix that.
The JavaScript history.back() function takes you to the previous synchronous request, not to the previous view as you seemed to expect.
Even though the history.back() is terrible this way (unreliable, hackable, etc), a quick fix would be to send an ajax request on form submit instead of a synchronous request.
<h:form>
...
<h:commandButton value="submit">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
</h:form>
Ajax requests doesn't account as browser history.
A more robust way is to just pass the identifier of the previous view along during navigation and then use <h:link> to link back to it. E.g.
<h:link value="Go to next view" outcome="nextview">
<f:param name="from" value="#{view.viewId}" />
<h:link>
And then in the nextview.xhtml
<f:metadata>
<f:viewParam name="from" />
</f:metadata>
...
<h:link ... outcome="#{from}" rendered="#{not empty from}">
<h:graphicImage ... />
</h:link>
If you're navigating by POST, you might consider using the flash scope to remember the initial view.
Unrelated to the concrete problem, the proper way to use <h:graphicImage> with JSF2 resources is to just use its name attribute instead of its value attribute with a #{resource} which is plain clumsy.
Replace
<h:graphicImage width="90" height="28" value="#{resource['images:blu_btnCancel.png']}" />
by
<h:graphicImage name="images/blu_btnCancel.png" width="90" height="28" />
Note that the library name images is here just replaced by the path name. The usage of the name "images" as library name is highly questionable. See also What is the JSF resource library for and how should it be used?

When is a f:param actually sent?

I've got the following h:commandButton:
<h:commandButton action="#{myBean.myAction}" value="Send">
<f:param name="myFlag" value="true" />
</h:commandButton>
I want to gain access to myFlag insinde a Validator, that's attached to another element with f:validator.
Unfortunately, when I want to retrieve the parameter through the FacesContext, I only get null returned.
Is it that the parameters are only sent once all validators have been invoked?
The <f:param> inside <h:commandButton> is only supported since JSF 2.0, but you're using JSF 1.2. The <f:param> is then only supported in <h:commandLink>.
<h:commandLink action="#{myBean.myAction}" value="Send">
<f:param name="myFlag" value="true" />
</h:commandLink>
There are alternatives, such as a <h:inputHidden> or <f:setPropertyActionListener> or <f:attribute> or in this case perhaps just plain <input type="hidden"> (the hidden input component and the action listener will only set their value during update model values which is later than validations phase; the attribute tag has better to be set on the component which invokes the validator). As the functional requirement is unclear, it's not possible to suggest the best alternative.
Update as per the comments, apparently all you need to know is if the particular button is pressed or not; in that case just give it and the parent form a fixed ID
<h:form id="form">
<h:commandButton id="send" ...>
this way it will have a fixed request parameter name of form:send and you could check on that in the validator:
if (externalContext.getRequestParameterMap().containsKey("form:send")) {
// Send button is pressed.
}
You can by the way also check for that in the required validators as follows:
<h:inputText ... required="#{not empty param['form:send']}" />
See also:
Action dependent requireness

How to do client side updates in JSF 2.0

I'd like to do client side component updates. Example: disable a button when a checkbox is clicked:
<h:selectBooleanCheckbox value="true" onchange="button.disabled=true" />
<h:commandButton id="button" value="Save" />
The above doesn't work. Is there a way to do this declaratively in JSF?
It doesn't work because the JSF component ID is not necessarily the same as the generated HTML ID. JSF composes the HTML element ID and form element name based on the ID's of all UINamingContainer components in the tree -if any. The UIForm component is one of them. Rightclick the page in webbrowser and choose View Source. See and learn.
There are two ways to solve this problem:
Set prependId attribtue of <h:form> to false.
<h:form prependId="false">
<h:selectBooleanCheckbox value="true" onclick="button.disabled=true" />
<h:commandButton id="button" value="Save" />
</h:form>
Note that I'd rather use onclick="button.disabled=!checked" instead. The onchange is only fired when the input element loses focus and that is non-intuitive in case of checkboxes and radiobuttons. Also, you'd like to turn it back on when the checkbox is checked again, right?
Give the <h:form> a fixed ID so that you know how the generated form element name look like:
<h:form id="form">
<h:selectBooleanCheckbox value="true" onclick="form['form:button'].disabled=true" />
<h:commandButton id="button" value="Save" />
</h:form>
The brace notation is mandatory because the : is an illegal identifier character and just onclick="form:button.disabled=true" won't work. Also here, I'd rather use onclick="form['form:button'].disabled=!checked instead.
The same problem applies on document.getElementById() as well. So simply replacing button by document.getElementById('button') alone won't fix the problem.
simply use javascript
document.getElementById("buttonId").disabled="true";
Check this

Resources