I'm stuck in a navigation case problem similar to this one.
In a few words, I'm trying to redirect navigation from one page to another, using an ajax rendered h:commandLink.
Here's the backing bean
#ManagedBean
public class StartBean {
public void search(){
FacesContext
.getCurrentInstance()
.getExternalContext()
.getFlash()
.put("result", "hooray!")
;
}
public String showResult(){
return "result?faces-redirect=true";
}
}
and the starting page
<h:body>
<h:form prependId="false">
<h:commandButton value="Click" action="#{startBean.search}">
<f:ajax execute="#this" render="#form"/>
</h:commandButton>
<br/>
<h:commandLink
action="#{startBean.showResult()}"
rendered="#{flash.result != null}"
value="#{flash.result}"
/>
</h:form>
</h:body>
whereas result page is just showing a message. Both pages are on web module context root.
It happens that the h:commandLink is correctly displayed after ajax submit, but clicking on it causes a page refresh. It doesn't redirect towards the result page, as expected.
After it, if page is reloaded (F5), result page is shown. It seems to be a rendering cycle matter.
Any suggestion?
Thanks in advance.
The rendered attribute of all input and command components is re-evaluated when the form is submitted. So if it evaluates false, then JSF simply won't invoke the action. The Flash scope is terminated when the request/response of the search() method is finished. It isn't there in the Flash scope anymore when you send the request of the showResult(). I suggest to put the bean in the view scope and bind the rendered attribute to its property instead.
#ManagedBean
#ViewScoped
public class StartBean {
private String result;
public void search(){
result = "hooray";
}
public String showResult(){
return "result?faces-redirect=true";
}
public String getResult() {
return result;
}
}
with
<h:commandLink
action="#{startBean.showResult}"
rendered="#{startBean.result != null}"
value="#{startBean.result}"
/>
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
Related
What is the correct way to return a value from a method in a managed bean indicating a successful SQL DML operation such as INSERT or UPDATE
Based on the tutorials and some blogs I read online, it says that a good practice in returning values from a method contained in a ManagedBean is a String value of a facelet or the .xhtml page
(in other words, a view) to redirect to a webpage.
Like this.
Original version:
#ManagedBean(name="myBean")
public class MyBean{
public String register(){
String SQL = "INSERT INTO myTable(col1,col2) VALUES(?,?);
PreparedStatement ps = connection.prepareStatement(SQL);
ps.setString(1,someStringVariable1);
ps.setString(2,someStringVariable2);
ps.executeUpdate();
return "index"; //pointing to index.xhtml
}
}
How about if i want to know if the executeUpdate() method was successful?
I thought I'd just change it to something like
Modified version:
public int register(){
int isSuccessful = 0;
try{
String SQL = "INSERT INTO myTable(col1,col2) VALUES(?,?);
PreparedStatement ps = connection.prepareStatement(SQL);
ps.setString(1,someStringVariable1);
ps.setString(2,someStringVariable2);
isSuccessful = ps.executeUpdate();
}
return isSuccessful;
}
However, on the modified version I returned an int which I don't see as correct as per how JSF should work with ManagedBean (Please correct me if i'm wrong)
I asked because say I have a Twitter Bootstrap modal which has the following.
<div class="modal-body">
<h:form>
//....some code here...
<h:commandButton value="Submit" action=#{myBean.register()}
<h:outputText value=" #{myBean.whatToPutHere_ToKnowIf_addData()wasSuccessful}" />
</h:form>
</div>
I need to be able to show within the modal div that register() execution was successful.
What is the simplest and correct way to accomplish that?
I read somewhere that AJAX will help but right now, I have very little knowledge of AJAX so maybe a simple solution to this is better.
I'd appreciate any help.
Thank you.
You can use a bean variable:
#ManagedBean(name="myBean")
public class MyBean {
private boolean isSuccessful = false;
public boolean isSuccessful() { return isSuccessful; }
public String register(){
isSuccessful = ...
}
}
and in your xhtml
....
<h:commandButton value="Submit" action="#{myBean.register()}" />
<f:ajax execute="#form" render="outtext" />
</h:commandButton>
<h:panelGroup id="outtext">
<h:outputText value="any Text" rendered="#{myBean.successful} />
</h:panelGroup>
</h:form>
I'm having some problem with Primefaces p:cache component
This is example how its used on testpage/index.xhtml
<h:form>
<p:panel header="Testsite">
<p:cache region="testsite2"
key="testsite2#{user.id}#{user.defaultLanguage}">
<p:commandButton action="#{testBean.hello}" value="btn"
rendered="#{testBean.renderedButton}">
</p:commandButton>
</p:cache>
</p:panel>
</h:form>
and this is back end bean
#ManagedBean(name = "testBean")
#ViewScoped
public class TestBean {
#PostConstruct
public void init() {
System.out.println("init");
}
public void hello() {
System.out.println("hello");
}
public boolean isRenderedButton() {
System.out.println("isRenderedButton");
return true;
}
}
So on first page hit init and isRenderedButton message are printed normally as expected. After that when I click on button I do expect to see hello message printed, but that's not case here. Can anyone point me in right direction ?
According to Primefaces showcase for p:cache with buttons I was expecting this behavior.
Right now I am using Primefaces.DEFAULT_CHACHE_PROVIDER and later I will switch to ehcache.
I'm using PF 5.3, sun faces 2.2.12.
Thanks.
To answer myself (and maybe ill help someone), i was trying to create dynamic menu from database and i wanted to cache generated content with p:cache component. But back then every menu item would call bean method which would redirect user to page and that was problem in first place.
So i had something like this:
<p:menu>
<p:submenu label="Human resource">
<p:menuitem value="Search person" actionListener="#{bean.navigateUserToSearchPerson}"/>
</p:submenu>
I actually did not fix this problem (had no extra time to investigate problem), so i came up with idea to generate links for each menu item, so when user clicks on menu item, it would redirect him to new page. So now i don't have any AJAX calls in menu and caching works fine now.
Code example:
<p:cache region="appUiCache" key="panelMenu#{user.id}#{user.defaultLanguage}">
<p:panelMenu id="sm" model="#{bean.menuModel}" stateful="true" />
</p:cache>
Menu items are created dynamically from database:
DefaultMenuItem defaultMenuItem = new DefaultMenuItem(...);
defaultMenuItem.setIcon(item.getIcon());
defaultMenuItem.setUrl(item.getUrl()); <!-- This is new url part -->
This works fine now in production. Thanks.
I have something like this :
<h:commandButton action="#{MyBean.action()}">
<f:ajax render ="#all"/>
</h:commandButton>
Is it possible to replace the "#all" in the ajax render to refresh the page?
With this code my page doesn't refresh like if I press F5
That's not possible with ajax. Just remove <f:ajax> and send a redirect to self in action method.
<h:commandButton value="Submit and refresh" action="#{bean.action}" />
public String action() {
// ...
FacesContext context = FacesContext.getCurrentInstance();
return context.getViewRoot().getViewId() + "?faces-redirect=true";
}
Or if you actually don't need to perform a business action, just use <h:button> without an outcome.
<h:button value="Refresh only" />
First of all, my beans are managed by spring not by JSF and I am using custom view scope as described in this article. So if the behavior is weird for regular JSF2 and might be related to Spring, please tell me.
Bean:
public class DepartmentBean {
private DefaultTreeModel model;
public void preRender(ComponentSystemEvent event) throws Exception {
if (model == null) {
model = myService.buildModel();
}
}
public String clear() {
// resetting stuff
return "pretty:";
}
}
View:
<h:form>
<ice:panelGroup styleClass="crud-links">
<h:commandLink value="Delete" action="#{department.deleteDepartment}" />
</ice:panelGroup>
</h:form>
<h:form>
<ice:panelGroup>
<ice:tree id="tree" value="#{department.model}" var="item" hideRootNode="false" hideNavigation="false" imageDir="./xmlhttp/css/xp/css-images/">
<ice:treeNode>
<f:facet name="content">
<ice:panelGroup style="display: inline">
<ice:commandLink value="#{item.userObject.text}"></ice:commandLink>
</ice:panelGroup>
</f:facet>
</ice:treeNode>
</ice:tree>
</ice:panelGroup>
</h:form>
When page is loaded for first time the model object is populated with data, but when clicking delete button I notice that after clearing preRender() method is executed and the model (which was populated before clearing becomes null, and gets populated again, although I am in same page, and it should maintain the value)
Does the code have a problem that leads to such behavior, or this is the normal behavior?
If the problem maybe related to Spring or the custom view scope or the IceFaces, please advise.
UPDATE- REQUIREMENT:
I want to initialize the tree model on construction of the page, and while i am still on the page the tree model doesn't gets initialized again until i do that programatically .
oh my mistake, initialization should be inside #PostConstruct.
I've got a JSF page with a ui:repeater tag that simply displays a list of strings and some controls to add a string to a list. When adding a string I use ajax to update the repeater tag and have the new string be shown immediately without the page refresh. Here's how my page looks like:
<h:body>
<h:form>
<p:inputText id="name" value="#{testController.newString}"/>
<p:commandButton value="Add" actionListener="#{testController.addString}" update="strings" />
</h:form>
<h:panelGroup id="strings">
<ui:repeat var="str" value="#{stringModel.strings}" varStatus="stringData">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:panelGroup>
</h:body>
Everything works except the inputText component. After ui-repeater is updated with Ajax is still displays the text from the previous string. For example, assume that initially i have a list with 2 strings, "val1" and "val2". I enter a new string called "val3" and submit the form. List is updated correctly on the server side and the repeater is updated, it now has 3 elements. However, while the h:outputText in the newly added element will correctly show "val3", the inputText will be displayed with "val2" as a value. So i end up with something looking like this:
output tag input tag
val1 val1
val2 val2
val3 val2 (???)
The backing beans are very simple:
A view scoped model bean
#Component
#Scope("view")
public class StringModel {
private List<String> strings = Lists.newArrayList("Value 1");
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
this.strings = strings;
}
}
And a request scoped controller bean:
#Component
#Scope("request")
public class TestController {
private String newString;
#Autowired private StringModel model;
public void addString() {
model.getStrings().add(newString);
}
public String getNewString() {
return newString;
}
public void setNewString(String newString) {
this.newString = newString;
}
}
I did some testing and this actually works the same way for any input component, be that textInput, textArea, etc. Any help would be highly appreciated.
I can't tell in detail exactly why it displays the wrong value after update (it'll be that the internal loop index of <ui:repeat> is broken — try a newer Mojarra version), but just referencing the string item by index from varStatus works. It'll also immediately fix the future problem of being unable to submit the edited string value when you put this list in a form, because the String class is immutable and doesn't have a setter.
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{stringModel.strings[loop.index]}" />
</div>
</ui:repeat>
EditableValueHolders inside ui:repeat are broken (by design) in the current version o JSF specs. It will not work, there is no way to fix it. Maybe new versions will make ui:repeat a proper component with support for saving states of its children. Maybe not.
If you change ui:repeat to h:dataTable, things should work (if not, then your problem is somewhere else and I was wrong).
Frankly, there is no workaround apart from using repeaters from some other libraries - you should find working repeaters in Tomahawk, Trinidad and many other places. Primefaces, AFAIR, does not have a pure repeater.
I also had exactly the same problem before. I solved it by putting the inputText in a form. I also copied your codes and put the h:inputText inside a h:form and it worked as well.
<h:form>
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:form>