Error trying to execute method after error catch - jsf

I have this jsf code
<h:form>
<h:inputText value="#{agreement.serviceId}"/>
<h:commandButton value="Enter" action="#{agreement.build}" />
<h:form rendered="#{!agreement.valid}">
<h:outputText value="Service id not valid. Please try again"/>
</h:form>
<h:form>
This is the scoped bean's build method.
public String build(){
try{
...//lots of backend logic
valid = true;
return "/agreementDetail.xhtml?faces-redirect=true";
}catch(Exception e){
valid = false;
return null;
}
}
Basically, here's the behavior I need:
The user inputs a serviceId. If this service id is valid, it redirects the user to the agreementDetail.xhtml page. If false, the user remains in the main.xhtml page and the "Service id not valid..." message is rendered.
This is what's happening:
If the user inputs a correct service id, everything works fine. If the user returns to main.xhtml and inputs an incorrect service id, the error is displayed correctly. But now, if the user inputs a correct service id, the build() method is not executed. (I've confirmed this with logging).
Basically, once the user inputs a wrong value, the build() method won't be executed ever again unless the user signs off and signs in again. Clearly, something's going on when the build() finds an error and catches the exception.
Any ideas?

You are nesting forms in your code. That is not allowed in JSF/HTML. You should replace the inner form with a <h:panelGroup> and everything should be fine.

Related

Form not submitting on a page with fragment 'rendered' conditions and ui:param

I'm creating a some sort of online shop with JSF. I have a product.xhtml page that displays the product by ID. I'm passing that ID as a param (.../product.xhtml?id=3) and I'm getting the ID with <ui:param name="productID" value="#{request.getParameter('id')}" />
That all works well.
Next, I'm showing and hiding certain elements in the page with <f:subview> (I've used <ui:fragment> before). The reason is that if the user deletes the ?id=3 ID parameter, the page will show an error (eg. code <f:subview id="main" rendered="#{productID != null and productID != ''}">). Another reason is that if the product belongs to the buyer, the BUY button will not appear and if the user is not authenticated the BUY button will not appear.
The problem is with the BUY button. It is in a form and the action of the button is just a simple test method (for now) from the CDI bean that prints something to the server console and redirects the user. Unfortunately, this does not work. The page (/product.xhtml) gets reloaded with no ID param.
I've tried several things like this and this and nothing is working.
I've tried using the ViewScoped and SessionScoped for my CDI bean instead of RequestScoped, but that does nothing. The ViewScoped fails to build.
I've also changed the <ui:fragment> to <f:subview>
Here's some code..
CDI bean controller
#Named
#RequestScoped
public class ProductManager {
...
public String buy(Product product) {
FacesContext context = FacesContext.getCurrentInstance();
try {
HttpSession session = Util.getSession();
User buyer = (User)session.getAttribute("user");
Date date = new Date();
System.out.println("TEST DATA: ");
System.out.println("sale product: "+product.getTitle());
System.out.println("sale buyer: "+buyer.getUsername());
System.out.println("sale date: "+date);
}
catch(Exception ex) {
System.err.println("ProductManager#buy -> "+ex.getMessage());
}
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "An error occured", null));
return "index";
}
...
}
Products.xhtml
...
<ui:param name="productID" value="#{request.getParameter('id')}" />
...
<f:subview id="buyBtn" rendered="#{user.username != login.username and login.regUser}">
<h:form style="margin-top: 30px;">
<b:navCommandLink styleClass="btn btn-info btn-block" disabled="#{!product.status}" value="Buy" action="#{productManager.buy(product)}"></b:navCommandLink>
</h:form>
</f:subview>
...
I can provide full code if needed.
What I'm expecting the code to do is that whenever I click on the BUY button, I'll get redirected to my page and the TEST DATA will be printed on server console.
After many. many attempts I solved this. I cannot find the exact explanation, as I read a ton of articles on this topic.
I used OmniFaces. Next in products.xhtml I changed h:form to o:form (from OmniFaces) and have these two set to true includeRequestParams="true" includeViewParams="true". My bean then became org.omnifaces.cdi.ViewScoped
This is also a great article

How to open a new jsf window in bean?

I am totally new about jsf page.
I want build a log in page, where you write in the correct password you will get to a new page, and if not it will show "invalid password".
now I can already validate if the password the correct using "if else" in the bean part. i figure if I can just direct to the next window it would be fine but I just cant get that part done... help !!
try this:
<h:commandButton action="page2" value="Page2" />
where page2 is the name of the page you want to show.
See also: http://www.tutorialspoint.com/jsf/jsf_page_navigation.htm
then do this:
<h:commandButton action="{YourBean.validateUser()}" />
In the bean:
public String validateUser() {
// Validate the user...
if (theUserIsValid) {
return "page2"
} else {
return null;
}
Where page2 is the name of the page you want to show when the user is validated.

Programmatically control which components should be ajax-updated

I have a complex form where the user fills a few fields, and has two options: generate a license file or save the changes. If the user clicks on the generate license file button without saving the changes, I render a small component with an error message asking him to save before generating the license.
To display the component with a warning message, I want to use ajax to avoid rendering the whole page just to render the warning component. Of course, if the changes were saved, then the warning message is not required and I redirect the user to another page.
I have a change listener on the changeable fields to detect when a change has been made. What I don't know is the conditional execution. The "render with ajax if unsaved OR redirect if saved" part. Here's the logic
if(saved){
redirect();
}else{
ajax.renderWarning()
}
--EDIT--
I'm going to add more info because I realized I'm leaving things too open ended.
Here's one example of an updateable field.
<h:inputText name="computername3" value="#{agreement.licenseServerBeans[2].computerId}" valueChangeListener="#{agreement.fieldChange}">
<rich:placeholder value="Add Computer ID"/>
</h:inputText>
The fieldChange() bean method
public void fieldChange(ValueChangeEvent event) {
change = true; //change is a boolean, obviously :P
}
Here's the generate license button jsf
<h:commandLink action="#{agreement.generateLicenseFile}">
<span class="pnx-btn-txt">
<h:outputText value="Generate License File" escape="false" />
</span>
</h:commandLink>
Here's the generateLicenseFile() method
public String generateLicenseFile(){
....//lots of logic stuff
return "/licenseGenerated.xhtml?faces-redirect=true";
}
Use PartialViewContext#getRenderIds() to get a mutable collection of client IDs which should be updated on the current ajax request (it's exactly the same as you'd specify in <f:ajax render>, but then in form of absolute client IDs without the : prefix):
if (saved) {
return "/licenseGenerated.xhtml?faces-redirect=true";
}
else {
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add("formId:messageId");
return null;
}
Returning null causes it to redisplay the same view. You can even add it as a global faces message and let the ajax command reference the <h:messages> in the render.
if (saved) {
return "/licenseGenerated.xhtml?faces-redirect=true";
}
else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(...));
return null;
}
with
<h:messages id="messages" globalOnly="true" />
...
<f:ajax render="messages" />

Navigate after successfull action on commandButton

I am building a JSF application using Primefaces mobile (0.9.3 with Primefaces 3.5).
I hava a login page, on this login page there is a login button defined as followed:
<p:commandButton value="#{msg['label.login']}" action="#{loginBean.login}" update="usernameMsg" />
The loginBean.login method is defined as followed:
public String login() {
try {
//do login
return "#loggedInTarget?transition=fade";
} catch (UserNotValidException e) {
//User not valid, display exception
FacesContext
.getCurrentInstance()
.addMessage(
"loginForm:username",
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"",
messageService
.getMessage("error.message.loginNotSuccessfull")));
}
return null;
}
In my main xhtml (the same in which the button is defined), I have defined the following addtional views:
<pm:view id="registerTarget">
<ui:include
src="/WEB-INF/c52bc19d58a64ae4bd12dec187a2d4b7/register.xhtml" />
</pm:view>
<pm:view id="loggedInTarget">
TEST
</pm:view>
I do it that way because I want to use transitions within Primefaces mobile. To get to the register page I use a p:button which works fine:
<p:button value="#{msg['label.register']}"
href="#registerTarget?transition=fade" />
Why is my login button not working? I also tried to put in "#loggedInTarget?transition=fade" directly as action, but this didn't work either :(
I need the action, because I have to check if the user credentials are valid. If they are valid I want to display my loggedInTarget.
How can I achieve it?
Thanks in advance!
EDIT:
The button is inside a form.
The navigation case outcome value must represent a view ID, not an URL (for sure not a hash fragment). Try with <p:button outcome="...">, and you'll see that it fails over there as well.
Your closest bet is sending a redirect.
public void login() throws IOException {
// ...
externalContext.redirect("#loggedInTarget?transition=fade");
// ...
}
This is also exactly what the <p:button href="..."> does under the covers: causing a GET request on the given URL.

How to set the exception message in a <h:message>?

I am trying to set an exception message in the <h:message>.
Here is the relevant view code:
<h:inputText id="titleId" value="#{bookController.book.title}"/>
<h:message for="titleId"/>
<h:commandButton value="Create a book" actionListener="#{bookController.doCreateBook}" action="listBooks"/>
I need a message to be displayed when the titleId is empty. My #Stateless EJB method throws an exception when the title is empty:
public Book createBook(Book book) throws CustomException {
if(book.getTitle().isEmpty()) {
throw new CustomException("Please, type a Title !");
}
else {
em.persist(book);
return book;
}
}
My backing bean catches it and sets a message:
public void doCreateBook() {
FacesContext ctx = FacesContext.getCurrentInstance();
try {
book = bookEJB.createBook(book);
bookList = bookEJB.findBooks();
} catch (CustomException e) {
ctx.addMessage("titleId", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", e.getMessage()));
}
}
What I except is, when the exception occurs, an error message must be displayed near the input text tag, but it isn't the case, the execution displays the page with list of books and the "Error" message displayed under the list, as shown below:
How can I get the full exception message to show up next to the input field?
Apart from the erroneous message handling which Thinksteep has already answered, your other mistake is that you're doing validation in an action method. This is not right. You should be using JSF builtin validation facilities instead. Whenever the JSF builtin validation fails, then the action method will not be invoked and the page will also not navigate. The enduser sticks to the current form and the message will appear in the therefor specified <h:message> tag.
In your particular case, you just need to set the required attribute.
<h:inputText id="titleId" value="#{bookController.book.title}" required="true" />
<h:message for="titleId" />
If you want to customize the default required message, use requiredMessage attribute.
<h:inputText id="titleId" value="#{bookController.book.title}"
required="true" requiredMessage="Please, type a Title !" />
<h:message for="titleId" />
Remove that input validation from the EJB method. It doesn't belong there. The EJB isn't responsible for that, the caller (which is in your case thus your JSF code) is responsible for that.
ctx.addMessage("titleId", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", e.getMessage()));
Your message text is Error and you are getting same. Change "Error" here to what ever you want.
PUT <h:messages showDetail="true" />

Resources