If I can't use f:viewAction within a JSF template, where can I put generic post-URL processing code? - jsf

I have a generic JSF page and abstract class that is implemented by many other pages and used to do its main processing in a #PostConstruct method. Now that I'm using URL parameters for some of the pages, I want to move all of the pages' processing into a viewAction action method instead so that it can use the URL parameters in their processing.
Of course, I can't use f:metadata/f:viewAction in my template because that's not allowed by JSF:
When using <ui:composition> templating, where should I declare the <f:metadata>?
Is there a way to handle post-viewAction processing by all of my pages in a generic manner? Right now I have it working with an f:event tag:
Template page:
<ui:insert name="metadata"/>
<h:head>
<f:event type="preRenderComponent" listener="#{controller.postProcessParams}" />
</h:head>
Client page:
<ui:define name="metadata">
<f:metadata>
<f:param name="id" value="#{manageProjects.id}"/>
<f:viewAction action="#{manageProjects.processParams}"/>
</f:metadata>
</ui:define>
<ui:param name="controller" value="#{manageProjects}"/>
Is this proper though?

Better use preRenderView instead of preRenderComponent, particularly if the method could potentially throw an exception and/or perform a navigation/redirect, otherwise you could potentially face an incomplete response and/or kind of "Response already committed" exception when it has taken place.
The preRenderView is basically like attaching preRenderComponent on <f:view>/UIViewRoot. I.e. before the entire view get rendered and thus guaranteed to run before any bit gets written to the HTTP response body. You'd rather not perform business logic while JSF is busy generating HTML output.
For the remainder, the templating approach is OK.

Related

Multiple <f:viewAction> in <f:metadata>

I am developing a CMS using JSF 2.3. I need to pass a GET parameter to every page indicating the site that the user is managing. To do this I am using <f:viewParam> in all pages, but I have the following doubts:
Is it OK to use multiple <f:viewAction> for multiple managed beans like the following example?
<f:metadata>
<f:viewParam name="form" value="#{editFormWebBean.formIdParam}"/>
<f:viewParam name="site" value="#{headerWebBean.siteIdParam}"/>
<f:viewAction action="#{editFormWebBean.init}" />
<f:viewAction action="#{headerWebBean.init}" />
</f:metadata>
This works, but I am not sure if it is OK.
Is there a way to avoid replicating in every page the <f:viewParam> for the site parameter? I tried with includeViewParams, but doesn't worked if I don't include the <f:viewParam> in the source and destination page (from page1.xhtml to page2.xhtml)
3) Can I define multiple <f:metadata> tags? For example if I am using templates and multiple ManagedBeans pare page (one for the header, one for the menu and so on).
Thank you and sorry about my english.
it's okay to have as many <f:viewParam> and <f:viewAction> as long as they work well together, but please make sure these are meant to initialize the view "JSF page" not the backing beans, use #PostConstruct on the baking beans to initialize them, also but in your mind that <f:viewAction> is only executed on GET request by default any subsequent POST (postback) requests don't invoke the action unless it has onPostBack="true" attribute.
more on these tags can be found in this great answer
[What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?][1]
put it in a template using the templating feature of JSF like this:
template.xhtml
<f:metadata>
<f:viewParam name="site" value="#{headerWebBean.siteIdParam}"/>
<ui:insert name="metadata"/>
</f:metadata>
page.xhtml
<ui:composition template="template.xhtml">
<ui:define name="metadata">
<!-- whatever metadata you want to add-->
</ui:define>
</ui:comosition>
no it's one metadata tag per page, use it like the example above.
[1]: https://stackoverflow.com/a/6377957/4649524

How to call the same method after every include of a .xhtml page in JSF?

I have the following part of a .xhtml page:
<ui:composition template="./templates/template.xhtml">
<ui:define name="mainContent">
<ui:include src="include/includeAbleEditor.xhtml">
<ui:param name="includeParam" value="MyClass" />
</ui:include>
<ui:include src="include/includeAbleEditor.xhtml">
<ui:param name="includeParam" value="YourClass" />
</ui:include>
</ui:define>
In the "includeAbleEditor.xhtml" I want to call a method after it was included (In this case this should happend two times).
Now I tried to solve it like this: (metadata tag is part of the includeAbleEditor.xhtml)
<f:metadata>
<f:event type="preRenderView" listener="#{editor.onload}" />
<f:attribute name="textFieldId" value="#{includeParam}" />
</f:metadata>
The Problem:
The method is being called only once. But it should be called two times. Once with the parameter "MyClass" and once with "YourClass".
Do you have any suggestions?
Thanks a lot!
There can be only one <f:metadata> in the entire view and it must be in the top level view. Unlike e.g. <f:view>, they don't "extend" each other and all others will be ignored.
You actually don't need it here. It's only necessary whenever you need to attach <f:viewParam> and/or <f:viewAction> to the specific view. The <f:event> doesn't require a <f:metadata>. It will just be hooked to the parent UIComponent. It was during JSF 2.0/2.1 ages (when <f:viewAction> didn't exist) being abused to have a hook to invoke a listener after <f:viewParam> values are being set. It's just for self-documentary purposes being placed in the same <f:metadata> as where all <f:viewParam>s are.
So, just get rid of it.
<f:event type="preRenderView" listener="#{editor.onload(includeParam)}" />
That said, postAddToView is likely a better event to hook this all on. And to avoid "Duplicate component ID" errors over all place later on, consider wrapping it in <f:subview> or making it a composite.
See also:
When using <ui:composition> templating, where should I declare the <f:metadata>?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Avoiding duplicate ids when reusing facelets compositions in the same naming container

JSF/CDI: General way to update page content

what is the general way when requesting new pages with given parameters, i.e. calling CDI bean operations and bind the outcome to the page components?
I am using this 'pattern', but is this the right way?
<ui:define name="content">
<h:form id="dataForm">
#{userForm.init(param.id, param.mode)}
<!-- User edit Dialog -->
<p:panel>
...
</:panel>
</h:form>
</ui:define>
The problem is that when using the 'rendered' attribute, this depends on the bean (non-blocking) process outcome but the page might be rendered faster. Thus, I should be able to call an update process on the page UI components after processing.
If you are using JSF 2.2 the way to go is:
<f:metadata>
<f:viewAction action="#{backingBean.action}"/>
</f:metadata>
If you are < JSF2.2 but you happen to be using Seam you could use something like this as a pre-render view event, it does not have to be in a template, you can drop it in your ui:composition
XHTML
xmlns:s="http://jboss.org/seam/faces"
<f:metadata>
<s:viewAction action="#{backingBean.action}" />
</f:metadata>
here is something to read Seam3, if not you can always use the classic way:
<f:metadata>
<f:viewParam name="id" value="#{backingBean.entryId}"/>
<f:event type="preRenderView" listener="#{backingBean.loadEntry}"/>
</f:metadata>

Getting viewParam in POST

Consider the following code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:o="http://omnifaces.org/ui">
<f:metadata>
<o:viewParam name="selected" value="#{testBacking.selected}" >
</o:viewParam>
</f:metadata>
<h:head>
<title>
title
</title>
</h:head>
<h:body>
<o:form includeViewParams="true">
<h:commandButton action="#{testBacking.go()}" value="go">
<f:ajax execute="#all" render="#all"/>
</h:commandButton>
</o:form>
</h:body>
</html>
The action method:
public void go() {
System.out.println("go() is called");
Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(FacesContext.getCurrentInstance().getViewRoot());
for (UIViewParameter viewParam : viewParams) {
System.out.println(viewParam.getName() +" = "+ viewParam.getValue());
}
}
The action method returns correct viewParam name, but the value always null. If this is the intended behaviour, how <o:form includeViewParams="true"> helps in practical usage?
I am using Mojarra 2.1.12 and Omnifaces 1.1.
Your bean is apparently request scoped. The <o:viewParam> is intented to be used in combination with view scoped beans only because it prevents the model being converted/validated/updated again and again on every postback to the same view even though it was already done during the initial request and is still there as property of the view scoped bean. This improves overall performance when the model is being bound to an expensive converter which performs the job based on DB calls. The showcase example also shows this clearly.
To achieve this, the <o:viewParam> basically skips this model conversion/validation/update job during any postback. A request scoped bean is however newly created on every request and the initial model value is thus lost on every postback and due to the design of <o:viewParam> not being set at all.
You can solve this by placing the bean in the view scope, or by just using the standard <f:viewParam> instead.
As to the <o:form includeViewParams="true">, this is only useful on synchronous postbacks. If you remove the <f:ajax> then you'll see how it's useful. Without includeViewParams="true" the URL becomes the one without the view parameter, i.e. without ?selected=somevalue and it becomes thus unbookmarkable. This has no strict relationship with <o:viewParam>, it works as good in combination with <f:viewParam>.

Does it matter whether place f:event inside f:metadata or not?

w.r.t. How to execute action on GET request with f:viewParam?
<f:metadata>
<f:viewParam name="id" value="#{tInputBean.id}" />
<f:event type="preRenderView" listener="#{tInputBean.init}" />
</f:metadata>
I'm interested to know whether it matters if a preRenderView f:event is placed inside the f:metadata or not. I've checked the Java EE6 tutorial, Java Server Faces 2.0 Complete Reference, and Core JSF2, and none of them have examples of f:event inside f:metadata, but I've seen lots of examples online like this.
JSF2 Compl.Ref says p.540
The f:metadata tag encapsulates the set of elements used to specify
the metadata for a Facelet view, and therefore must be a child of the
f:view tag and may not appear in a template. As of JSF2.0, the only
purpose of this tag is to encapsulate f:viewParam tags.
Does placing the f:event (often used to support an f:viewParam) inside the f:metadata have a special meaning, or is it just to help group it alongside the f:viewParam visually/logically ?
No, the <f:event> is not strictly required to be placed inside <f:metadata>. It can be attached to any component. It's indeed for pure self-documentary purposes placed inside the <f:metadata> whenever you have a bunch of <f:viewParam>s and would like to hook a <f:event> to invoke an action after all those view parameters have been set. It can even be placed outside/before those <f:viewParam>s, but it makes the code not more self-documenting.
Note that in the upcoming JSF 2.2, a new <f:viewAction> tag will be introduced which in turn is supposed to replace the <f:event type="preRenderView"> in the <f:metadata>.

Resources