I have a legacy code that I am about to rewrite. It contains a lot of custom JSF components. Most of them simply builds a subtree of other components (either from JSF core or other customs) in place when they are in the tree. So for example in xhtml I have:
<custom-component atributes...>
<child-component/>
<child-component/>
</custom-component>
And then in PostAddToViewEvent implementation of this component does for example something like:
UIComponent rootChildElement = JSFUtil.findAncesstorOfType(this, HtmlForm.class);
List<UIComponent> guiListChildren = this.getChildren();
if (rootChildElement == null) {
rootChildElement = new HtmlForm();
} else {
rootChildElement = new UINamingContainer();
}
rootChildElement.getChildren().addAll(this.getChildren());
But during debugging I have noticed that when JSF processed an AJAX request for such custom component, that even though children are added to the created form, they are built again from XHTML page and added again. This results in those children being actually in two places in the tree when AJAX is performed.
Funny thing that this works in JSF 2.0.3, but when we wanted to upgrade to JSF 2.1.something it stopped working, because command link in the children which was AJAX source could not be found in the tree when it is expected to be.
I think it should be possible to do the same thing in PreRenderView phase, but then actions won't work, because command components will be created too late.
How such components should be implemented:
composite component
facelet tag
tag decorator?
something else?
Is it safe to modify component tree here? If I have a component, that do not reassign children, but adds some new items to itself like command buttons or other components?
Related
I'm on Mojarra 2.2.13 and my project uses PrimeFaces 6.0.
I'm writing my own JSF UIComponent. It requires a bit of JavaScript located in webapp/resources/js/charts.min.js. When I annotate my component using #ResourceDependency the script is rendered:
#ResourceDependency(name = "js/charts.min.js", target = "head")
But, I don't always need it to be rendered. So I was trying to conditionally add a component resource to the view root from within the encodeBegin(FacesContext context) method:
if (condition) {
UIOutput js = new UIOutput();
js.setRendererType("javax.faces.resource.Script");
js.getAttributes().put("name", "js/charts.min.js");
context.getViewRoot().addComponentResource(context, js, "head");
writer.startElement("div", null);
writer.writeAttribute("class", "myChart", null);
// ... write chart data
writer.endElement("div");
}
This does not render the script (myChart is rendered though). No errors appear in my log. Any ideas what I could check or improve?
I've also tested without PrimeFaces (not sure if its head renderer was causing this), but the result is the same.
So, encodeBegin(FacesContext context) is not the correct location to add resources. You are too late there.
I've moved the code to the constructor of the component and now the script is added. I'm not 100% this is the best location to do so, but I've seen component libraries doing it in the constructor as well. It also works together with PrimeFaces.
See also:
How to programmatically add JS and CSS resources to <h:head>?
I'm new to JSF, I've read some of the specs but there are still many things I don't understand.
I have joined a project with JSF1.2 + Richfaces 3.3 + some EL that allows to call methods with parameters.
I have created a template xhtml that is meant to be reusable, and I have a bean that holds all the data the template needs to build and render itself. The bean is referenced in the controller.
The template take 2 parameters, the controller and a string value feature that is used to produce the bean that holds the data. I do something very ugly to invoke the method that will produce the bean on the controller :
<h:outputText value="#{controller.loadFeature(feature)}" />
Then I use the bean that is referenced by the controller to build the view
<ui:include src="#{controller.myLoadedBean.widgetPath}">
I need this bean at the build time because depending on the data, I have to include other nested xhtml templates. But I don't know how to manage this :
If the controller is request-scoped, the loadFeature method is invoked too late, at the RENDER_RESPONSE phase, the view is not built with the loaded data
If the controller is session-scoped, it's even worse, the loadFeature method in invoked at the RENDER_RESPONSE, so the first time I try to load the page I have an error, and the the pages are build with the data of the previous request.
Basically, what I need is to be able to call a method of the controller with a template parameter at the view build time.
I don't know how to achieve this, or if it is possible at all. Maybe there are some other mechanism in JSF I don't know of that allow a controller to be invoked before/when the view is being built.
Any help would be appreciated, thank you
I finally got my template working. I was wrong, JSF and Facelet components should not call a method so they can fill themselves.
The correct MVC way is to call a method of the controller before, the controller will build the view bean and then only redirect to the JSF view.
Having gone through these excellent posts:
Why JSF saves the state of UI components on server?
Why does JSF save component tree state?
and midway the JavaEE6 tutorial I still have the following questions:
When I am developing a custom UI component whose values (styleClass, value, etc) are either defined statically(in the xhtml) or set via a bean, do I need to explicitly save/restore state in the extended component as well?
Is it correct to say that the scope of the UI components is view scoped?
How is the view identified behaviour? (If I navigate away from a view, the view gets rebuild the next time around. But if I open another tab, it is restored - at least the bean!)
When I am executing an Ajax call, I would expect that 'execute' part of the UI component would be restored&processed and the 'rendered' part would be restored&updated. After running into some problems with UI:repeat, it is not clear to which extend the component tree is to be restored and if is possible to partially edit.
As an example (I am not sure that it works like this): I define a UI:repeat that iterates over some values and creates some Ajax commandlinks. Whenever I call the command, it will restore the whole ui:repeat regardless of the Ajax scope (execute/render) that I have defined. So it will re-render the whole ui:repeat. Furthermore, I don't understand how it could ever -not- restore the ui:repeat as due to being a namingcontainer it will edit the id of my newly added component.
How can I define a build-time component (vs render-time) and why would I want to do this? (It seems that build time components are troublesome when mixed with rendertime, so why have both)
Thanks
When I am developing a custom UI component whose values (styleClass, value, etc) are either defined statically(in the xhtml) or set via a bean, do I need to explicitly save/restore state in the extended component as well?
Yes. You normally use StateHelper for this.
See also:
How to save state when extending UIComponentBase
JSF custom component: support for arguments of custom types, the attribute setter is never invoked
Adding Custom Attributes to Primefaces Autocomplete Component in JSF
Is it correct to say that the scope of the UI components is view scoped?
Absolutely not. UI component instances are request scoped. Only anything which is stored via StateHelper is in essence view scoped (and restored into newly created component instances during "restore view" phase).
See also:
JSF composite component - weird behavior when trying to save state
Backing bean in composite component is recreated on every request
How is the view identified behaviour? (If I navigate away from a view, the view gets rebuild the next time around. But if I open another tab, it is restored - at least the bean!)
It's likely requested from browser cache. Try submitting a form therein. The chance is big that you get a ViewExpiredException. You need to tell the browser to not cache dynamic pages. Putting a breakpoint on bean's constructor would also confirm that it's never been invoked.
See also:
Avoid back button on JSF web application
Is JSF 2.0 View Scope back-button safe?
javax.faces.application.ViewExpiredException: View could not be restored
When I am executing an Ajax call, I would expect that 'execute' part of the UI component would be restored&processed and the 'rendered' part would be restored&updated.
This is not true as to restore part. The "whole" view state is restored. Note that the view state does since JSF 2.0 not necessarily represent the entire component tree. You've found the explanation/answer to that already in the two links mentioned in your question.
How can I define a build-time component (vs render-time) and why would I want to do this? (It seems that build time components are troublesome when mixed with rendertime, so why have both)
This is called a "tag handler". I.e. just extend from TagHandler instead of UIComponent and implement according its contract. Tag handlers are useful if the sole goal is to build the view (the JSF component tree). They do not appear in the JSF component tree. As to when to create a custom component or a custom tag handler, check the "components" and "taghandlers" sections of OmniFaces showcase, it may give some new insights as to real world use cases of those things.
See also:
Custom Facelet component in JSF
JSTL in JSF2 Facelets... makes sense?
I'm trying to add some functionality to the rich:dataTable by extending its renderer. More specifically, i'd like to render a link in the header column that triggers an ajax call (to trigger an action in a backing bean).
So my fist thought was to modify the dataTable's renderer to instantiate and add an a4j:commandLink instance. That does work to some extend, the link does get rendered just fine, however the associated action is not being called, and an added parameter assignemnt is not performed. furthermore i cant really make out what portions of the view are being executed and rendered, it seems the values i set are being ignored (for example if i specify #all as a value for those two properties i still get a partial response containing just a tiny fraction of my page).
This is the code i use to create the link:
UICommandLink l = (UICommandLink) context.getApplication().createComponent(UICommandLink.COMPONENT_TYPE);
l.setTransient(false);
l.setRender("#all");
l.setExecute("#all");
l.setActionExpression(ULHelper.createMethodExpression("#{orderBy.toggle('"+ sortName + "')}", null, String.class);
l.setParent(uiColumn);
l.getChildren().add(headerFacet);
l.encodeAll(context);
uiColumn is the UIColumn instance, headerFacet is the text that should be rendered within the link.
The link does get displayed correctly, a click on the link triggers an ajax request just fine (no errors in the javascript console) - but i have no idea what happens next on the server side, the action is not getting called, the table is not being re-rendered either.
If i add an ActionListener
l.addActionListener(...);
or add a parameter instance to the link
UIParameter par = (UIParameter) context.getApplication().createComponent(UIParameter.COMPONENT_TYPE);
par.setValue(sortName);
par.setAssignToExpression(ULHelper.createValueExpression("#{bean.text}", String.class));
l.getChildren().add(par);
it makes no difference, neither of the two are executed.
I suspect it has something to do with state saving, that the a4j:commandLink does not get saved and therefore the 2nd request does not find that link (and therefore the listener, parameter etc...) to work with, but i have no idea how to persist (and restore) the state of the link.
Just sample from working sources, which may be helpful:
import org.ajax4jsf.component.html.HtmlAjaxCommandLink;
public UIComponent ajaxLink(...) {
HtmlAjaxCommandLink ajaxLink = new HtmlAjaxCommandLink();
ajaxLink.setId(...);
ajaxLink.setValue(...);
ajaxLink.setAjaxSingle(true);
ajaxLink.setOncomplete(...some js func..);
ajaxLink.setReRender(...);
FacesContext context = FacesContext.getCurrentInstance();
MethodExpression actionListenerExpression = context.getApplication().getExpressionFactory().createMethodExpression(context.getELContext(), "#{someSeamComponent.someAction}", null, new Class[]{ActionEvent.class});
MethodExpressionActionListener actionListener = new MethodExpressionActionListener(actionListenerExpression);
ajaxLink.addActionListener(actionListener);
return ajaxLink;
}
uiComponent.getChildren().add(ajaxLink(...));
I have some code generated by netbeans wizard 'JSF pages from entity classes'.
For those who don't use netbeans I will briefly describe what the wizard does.
It creates a JSF page List.xhtml which contains a datatable with a fixed size of ten rows and two commandlinks for scrolling its content (prev 10 and next 10).
The JSF page uses a managedbean with session scope and the above mentioned commandlinks return the String 'List'.
If I change the managed bean scope to #viewscoped it is re-created every time I push a commandlink. According to me it is a weird behavior because the view actually doesn't change (it always List.xhtml ) and I would have expected a smarted view scope mechanism.
I then changed the action associated to the commandlinks with a new one which does the same things but returns void. Now the #viewscope mechanism works well but I'm not sure it is correct to use an action method with a signature like this
public void doSomething()
// instead of
public String doSomething()
My concern is that a different JSF implementation can act in an impredictable way with that kind of actions.
Thanks
Filippo
What do you return in public String doSomething()?
If you return null (or an empty String) it should not re-create the view.
If you return a navigation case then the view is re-created, regardless whether it is the same that you are currently in.