How to render view component after being altered in the managedbean? - jsf-2

I have an outputtext showing the number of services on the screen:
<h:outputText
value="Services #{bean.counterManager.serviceCounter}">
</h:outputText>
and below it an accordionpanel that calls the getServices() method:
<p:accordionPanel value="#{bean.services}" var="service">
In the getServices() method I increment the counter and when I debugged at the return point it was 143.
public List<Service> getServices()
{
if (this.services.isEmpty())
{
//Does other stuff, fills this.Services
this.counterManager.incrementServiceCounter(someValue); //
}
return this.services;
}
But it appears 0 on screen, because getServices() is called after outputText calls getCounterManager() probably because the outputtext is above the accordionpanel on my XHTML.
I'd like for serviceCounter to show 143 and not 0, but I don't how to make it render after getLinhasStruct() is called, I can't put it the outputtext below accordion panel because that would mess with the layout of the page so how can I do that?

Never do business logic in getters. You need to make sure that all your getters (and setters) are pure getters (and setters).
public List<Service> getServices() {
return services;
}
After your IDE autogenerates them in the very bottom of the bean class, just ignore them forever. Do not touch them. Do as if they do not exist. You're supposed to perform business logic in action event listeners.
Your concrete problem is caused because those getter methods are during render response phase called in the same order as the components appear in the tree, and your code is incorrectly relying on something which is not controllable from inside the backing bean.
You did nowhere state the concrete functional requirement in the question, so it's a bit hard to point out the right approach, but there are in general the following approaches depending on when exactly you'd like to perform the business logic.
During initial GET request? Use <f:viewAction>.
Right before rendering the view? Use <f:event type="preRenderView">.
During bean's initialization? Use #PostConstruct.
See also:
Why JSF calls getters multiple times
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
When to use f:viewAction / preRenderView versus PostConstruct?

Related

How to trigger destruction of viewscoped bean?

I have a #ViewScoped-annotated managedbean whose #PostContruct-method fetches a list from database to be displayed in a table in the view.
Now when I delete an item I want the changes to be seen in the view.
To keep this dynamic and reusable I only want to delete from database (not manually from list). So I need to destroy/recreate the bean I suppose. Now I do this by navigating to the same view. But the way I do is not reusable.
Can I just destroy the bean manually or navigate to the same view without explicitly navigating to THAT specific view (reusability)?
I am using JSF 2.1
You're already on the right track. viewMap is just like any other map; You can remove a ViewScoped bean by name. Please excuse the atrocious chaining:
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("yourBean");
One solution I found is to destroy the bean by
FacesContext.getCurrentInstance().getViewRoot().getViewMap().clear();
I am not sure if this is the way to go because it simply destroys every viewscoped bean. It's not that bad in this case, but doesn't feel clean.
I appreciate any thought on that or alternative solutions.
When you return a non null value inside a method from an action attribute the Bean gets recreated.
index.xhtml
...
<h:commandButton value="delete" action="bean.delete" />
...
Bean.class
...
public String delete() {
// do operations
return "index.xhtml?faces-redirect=true";
}
...

Dynamic breadcrumbs with primefaces

I would like to add to my web app a dynamic breadcrumb using the Primefaces component. I've created a model to push items on the breadcrumb so that when one of its links is followed, the trailing links are removed. This works in most scenarios, but sometime the bradcrumb doesn't behave the way I expect. Basically, in order to track the landing page, I've added a preRenderView listener on each navigable page and implemented the model update logic in a session scoped bean.
<f:event type="preRenderView" listener="#{bcb.onRenderView}" />
<f:attribute name="pageName" value="ThisPage" />
The listener receives the page name as an attribute and obtains the complete URL (including query string) from the external context; these information, along with a unique id created from the UIViewRoot, are used to build a BreadCrumbItem that is pushed on the model:
public void onRenderView(ComponentSystemEvent evt) {
UIViewRoot root = (UIViewRoot)evt.getSource();
final String reqUrl = FacesUtils.getFullRequestURL();
String pageName = (String) evt.getComponent().getAttributes().get("pageName");
if(pageName != null) {
model.push(new BreadCrumbItem(root.createUniqueId(), pageName, reqUrl));
} else {
model.reset();
}
}
The push() and reset() methods of the model are implemented as follows:
/**
* When a link is pushed on the bread crumb, the existing items are analyzed
* and if one is found to be equal to the pushed one, the link is not added
* and all the subsequent links are removed from the list.
*
* #param link
* the link to be added to the bread crumb
*/
public void push(BreadCrumbItem link) {
boolean found = removeTrailing(link);
if(!found) {
addMenuItem(link);
}
}
/**
* Reset the model to its initial state. Only the home link is retained.
*/
public void reset() {
BreadCrumbItem home = new BreadCrumbItem();
removeTrailing(home);
}
Is this approach feasible? Can you suggest some better way to track page navigation without the need to leverage a life cycle listener? Thanks a lot for your help.
I have implemented my own one for my web app, in my case I didn't use the p:breadCrumb component because it's implemented using buttons.
Basically, I have an #SessionScoped bean which contains a stack (navigation stack), storing all the url's you have in the breadcrumb and the params for each of them. The view (xhtml) part is composed by p:button elements, which have the outcome of the stack's stored urls.
When you navigate to an url, corresponding bean's f:event type="preRenderView" is called (as the way you're doing it) and the bean takes the params from the url, after that it establishes itself into the stack (not the bean itself, cause it's #ViewScoped and going to be destroyed, just the url and params).
In case you click on a back button in the breadcrum, you send an additional param which indicates the index of the button. Based on that index, the destination bean knows that you're trying to recover such view, so asks the navigation stack for that view params and navigation stack removes navegables which are after it.
It took me a while, but it's fully functional. Good luck.
Edit
Be careful when using session scope for saving the current navigation state. It'll have influence in all the opened tabs, so probably it's not what you want, unless you expect your end user to use your application in one tab only. Anyway, the general usability guidelines say you should use categories instead of navigation paths for your breadcrumbs (HTTP is stateless and the history itself is kept by the browser). So a dynamic breadcrumb doesn't make sense anymore, at least if you're using different urls for your views.

How to change the standard href="#" attribute of h:commandLink?

I am maintaining a JSF2 Ajax application and we are heavily using h:commandLinks and f:ajax tags for all actions - always only rerendering what is needed.
This does of course break the expected behaviour for the user when performing a right click on the links and choosing "Open Link in New Tab" etc.
I understand that f:ajax forces the href atribute of the resulting a element to be # and does all the magic post request trickery in the onclick function - I now want to provide fallback support for the "Open Link..." action by putting some meaningful link in the href attribute of the resulting <a> tag.
This would not break the "normal" onclick behaviour as the generated javascript always finishes with return false; but would allow me to send my users to some page using a normal GET request in case they want to open the link in a new window.
Is there a build in way to do this? Or could somebody point me in the right direction on where in the JSF lifecycle I would have to jump in to do this maybe using a phase listener?
Simplest would be to extend com.sun.faces.renderkit.html_basic.CommandLinkRenderer and override the renderAsActive() method accordingly. Mojarra is open source, just copy the method and edit the line where it says writer.write("href", "#", "href"). Replace the "#" string accordingly to your insight.
public class MyCommandLinkRenderer extends CommandLinkRenderer {
#Override
protected void renderAsActive(FacesContext context, UIComponent command) throws IOException {
// ...
}
}
To get it to run, register it as follows in faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.Command</component-family>
<renderer-type>javax.faces.Link</renderer-type>
<renderer-class>com.example.MyCommandLinkRenderer</renderer-class>
</renderer>
</render-kit>
Note that this tight couples your renderer to Mojarra. To be JSF implementation independent, you'd need to create a whole new renderer instead of extending a Mojarra specific renderer class.
Unrelated to the concrete problem, consider reading When should I use h:outputLink instead of h:commandLink?

Confused about JSF Lifecycle

I want to show a page that renders a list of 'Group' entities, so my page (xhtml) has this in it
<f:event type="preRenderView" listener="#{groupController.loadGroups}" />
This is called when the page loads, and yesterday it was returning a list as expected. The method looks like this
public void loadGroups() {
allGroups = groupDao.findAll();
}
Now I added a form to save a new group (on the same page). When I save that form, it calls this method:
public String save() {
groupDao.create(group);
return "/admin/groups?faces-redirect=true";
}
I have confirmed that the group is saved in the database. But, I was expecting the "return" statement to reload the page as if entering it for the first time, calling the 'preRenderView' method etc etc.
When it comes back into the page it does call that method, but am using a datalist to display the collection of groups returned and when that makes a call to
public Collection<Group> getAllGroups() {
return allGroups;
}
... the 'allGroups' variable has been reset back to null.
I haven't touched JSF for a couple of months and I think I used to understand all this but I obviously don't. Could anyone give me a clue what I'm doing wrong?
If I find the answer (digging my books out right now) I will post an update. Thanks though.
Update: just to clarify, I thought the loadGroups method would be executed before the getAllGroups method, since it's a preRenderView event. But they seem to be getting executed the wrong way around (getAllGroups is called first, before loadGroups() has populated the collection). It's a ViewScoped bean by the way.
I'm not sure why my original version doesn't work, but if I get rid of the preRenderView statement in my page, and add a #PostConstruct annotation on the loadGroups method, it works fine. I understand why the #PostConstruct method would work, but not why the preRenderView doesn't work. Anyway, its working if I do that...

JSF2 + IceFaces 2 - Retrieve UIComponent from ViewRoot

I've got hard time resolving the following. My problem is quite simple : I would like to highlight in red the forms fields that triggered validation errors. The error messages are placed correctly in the FacesContext using a context.addMessage(...) line.
I'd like my system to be generic. All form fields having a message attached are automatically highlighted.
I've found on this site a link to this excellent article :
http://www.jroller.com/mert/entry/how_to_find_a_uicomponent
With it, I did implement a PhaseListener for the RENDER_RESPONSE phase, which do the following :
#Override
public void beforePhase(PhaseEvent event) {
// get context
FacesContext context = event.getFacesContext();
// iterate on all the clientIds which have messages
Iterator<String> clientIdsWithMessages = context.getClientIdsWithMessages();
while (clientIdsWithMessages.hasNext()) {
// get the clientId for the field component
String clientIdWithMessage = clientIdsWithMessages.next();
// split on ":"
String[] splitted = clientIdWithMessage.split(":");
UIComponent component = findComponentInRoot(splitted[splitted.length - 1]);
if (component != null) {
Map<String, Object> attributes = component.getAttributes();
if (attributes.containsKey("style")) {
attributes.remove("style");
}
attributes.put("style", "background-color: #FFE1E1;");
}
}
}
This perform perfectly well for almost all my usage.
Now, where it becomes a bit tricky, is that some of my forms have such code :
<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
<ice:column>
<ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse" />
</ice:column>
....
The generated form has several lines (one for each object of the revisionsDocuments list), and each element has a unique identifier (clientId) which looks like :
contentForm:revisionDocuments:0:revisionSequenceAdresse
With 0 changed for 1, 2, ... for each iteration.
Consequently, the code provided to search the UIComponent from ViewRoot does not work properly. All forms fields have the same "id". What surprise me more is : they have the same "clientId" in FacesContext too :
contentForm:revisionDocuments:revisionSequenceAdresse
I cannot distinguish, while going through the tree, if I do see the right form field or any of the others.
Does anyone have a hint to solve this ? Or another suggestion to implement the highlight of my fields ? I have to admit, I dont really like my code, I consider dirty to manipulate the viewRoot like I'm doing, but I could not figure out a better solution to have a generic highlight of my fields.
I'm running IceFaces 2.0.2 with JSF-Impl 2.1.1-b04 on JBOss AS 7.0.2.Final.
Thank you in advance for the answers.
Best regards,
Patrick
You should apply this in the client side instead. You've got a collection of client IDs with messages. One of the ways is to pass this information to JavaScript and let it do the job. You can find an example of such a PhaseListener in this article: Set focus and highlight in JSF.
Since JSF 2.0 there is however another way without the need for a PhaseListener. There's a new implicit EL variable, #{component} which refers to the UIComponent instance of the current component. In case of UIInput components, there's an isValid() method. This allows you to do something like:
<h:inputText styleClass="#{component.valid ? '' : 'error'}" />
with this in a CSS file:
.error {
background: #ffe1e1;
}
(yes, you can also do this in a style attribute, but mingling style with markup is a poor practice)
To abstract this away (so that you don't need to repeat it in every input), you can just create a composite component for this, something like <my:input>.
For completeness, here is the solution I finally found to highlight the fields that do have error messages with IceFaces 2.0.2 :
The basic idea is strictly the same than proposed by BalusC on http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html
The piece of code I had to change with IceFaces is the small Javascript call :
<script>
setHighlight('${highlight}');
</script>
I could not find any IceFaces component which is re-rendered at each JS call. I found that placing the script into a panelGroup works most of the time. However, there was a case that did not work :
submitting the form with errors do trigger the JS.
then, re-submitting the form with errors on the same field than previous validation do NOT trigger the JS.
then, re-submitting the form with any error field having no more errors do trigger JS.
then, re-submitting the form with any non-errored field having an error do trigger JS.
For some reason, IceFaces do not render the panelGroup that contains the JS when the set of fields having errors is the same between two calls.
I tried to use the Javascript API with code like Ice.onAsynchronousReceive(), using Prototype library to attach an event to the AJAX completion of the commandButton, but had not much success with it. Some of my tests could run (with errors but did the job) and I could observe similar behavior.
Here is the trick I finally used (ugly but working) :
<ice:panelGroup>
<script type="text/javascript">
var useless = '#{testBean.time}';
setHighlight('${highlight}');
</script>
</ice:panelGroup>
The getTime() function simply return the current timestamp. The value is then always different and trigger the JS execution at any AJAX request.
Sadly, IceFaces do not have the RichFaces useful "oncomplete" attribute, which I do regret highly for this case.
Ugly solution, but funny and working.

Resources