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>?
Related
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?
How can I get the posted form data in the backing component in the
processUpdates method?
#Override
public void processUpdates(FacesContext context) {
//get here rendered html code
}
Or can I get the posted form data in the decode method?
[Edit]:
My goal is to get the posted form data - Not to get the generated html code (Sry I wasn't precisely)
It is unclear what you want to achive, yet. I mean, at high level.
UIComponent.decode and processUpdates are medium-level lifecycle APIs which should be overriden when you want to extend the framework.
If you just need to use the framework, you need a managed bean, not a backing component.
Furthermore, generally only components that extend UIInput need to hook in those phases, because they are bound to a value="#{...}" value expression (which in turn refers to a managed bean, in most cases), and need to synchronize those values with the bound expression.
I suspect you are uselessly complicating your life: hooking into medium or low-level APIs is a real pain if you don't have an excellent understanding about how the framework operates.
Anyway, the standard request parameters decode into input component is this:
String clientId = this.getClientId(context);
Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();
String newValue = requestMap.get(clientId);
if (newValue != null)
{
this.setSubmittedValue(newValue);
}
Please, post the full xhtml facelet code (not the composite one, but the facelet using that composite), so I can understand where you want to go and I can try to point you to the right tool to use.
I am using jsf2 and RichFaces. I want to track each page being browsed by the user.
For that I have created Servelet Filter which Intercepts the page being requested. In my project, I am using jsf template features where header and Footer are fixed. In the body part, I have defined menu.xhtml and an iframe tag. Response is targeted on to iframe whenever user clicks on any link on the menu.
My Problem is that, I am not getting the correct url of the page requested in the filter.
My Filter Snap
Below Shown is the Filter,looking For xhtml Page.
chain.doFilter(request, response);
HttpSession session = req.getSession(false);
if( null != session && (uri.contains(".xhtml") || null != session.getAttribute("userid"))){
if(null != session.getAttribute("userid")){
String userid = session.getAttribute("userid").toString();
//for saving usage details
if(uri.contains(".xhtml")){
System.out.println(".......Requeted Page.........."+req.getRequestURL().toString());
saveUserUsage(req);
}
}
}
url getting in the filter is userdeskop.xhtml even though different links in the menu are selected.
Reason for the same url independent from the clicked menu might be the JSF Lifecycle:
it decides on server side on which page to deliver.
From that side, independent from what you click on e.g. a JSF Mojarra Implementation, the requested page might always be the same - just the parameters differ ... and the server does a redirect to the desired page (which is just too late for your filter to be recognized ;-) ).
Update: I would try to get a phase listener being executed before or after RENDER RESPONSE phase, because there the navigation goal should be resolved. Within the listener something like (untested example code)
public void afterPhase(PhaseEvent event)
{
FacesContext context = event.getFacesContext();
String viewId = context.getViewRoot().getViewId();
....
}
might help you resolve the final url.
If you only want to resolve the urls for the main menu (I guess, that links are static and no managed bean method needs to be invoked), you can alternatively use h:outputLink, which resolves to fixed urls ( see When should I use h:outputLink instead of h:commandLink? for details) - this will work with your already existing listener.
Hope it helps...
I'm creating custom components using FXML. The custom components are designed in a hierarchical fashion.
When I design a custom component B that uses another custom component A, a classpath problem dialog pops up in scenebuilder and I simply fix this by setting the appropriate classpath.
However when I create three components, say C containing B containing A, and try to open top-level component C in Scenebuilder it fails. It asks me for classpaths which I duly specify. It finds B but does not find A.
The classpath, FXML and the code is correct as the application is able to execute properly. Only Scenebuilder is having problems.
How should one open hierarchical custom component with Scenebuilder?
Any reference to an example with hierarchical component definitions using FXML would be greatly appreciated and get a bounty of 50 points. (only 3 levels needed)
Someone named David did answer your question on the forum.
For legacy purpose I post it here.
There is a problem with the classloader in Scene Builder for custom components.
When you load a FXML file in SceneBuilder: it uses a FXMLLoader with its own classloader.
In order to load custom components which use their own FXMLLoader to load other custom components, it is necessary to make all FXMLLoader use the same classloader.
As David said on the forum, you can achieve that by adding this code in your custom component.
public class CustomC extends VBox {
public CustomC() {
init();
}
private void init() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setLocation(this.getClass().getResource("CustomC.fxml"));
// Make sure to load "CustomC.fxml" with the same classloader that
// was used to load CustomC class.
loader.setClassLoader(this.getClass().getClassLoader());
try {
final Node root = (Node)loader.load();
assert root == this;
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
If you want to externalize this code in a class, it is important to put this class in the same jar as your custom components: you cannot put it in a external jar (at least for now).
The JSF component SelectOneRadio layout is very limited so I wrote a custom Renderer for it, and it works great. However, there are times when I want to use the standard SelectOneRadio layout as well. So I decide to make my new component that utilize the custom Renderer I create, but I want this new component to mirror the functionality of SelectOneRadio, and the only different is that it will use my Renderer. Do I need to create both custom tag and custom component to go with my custom renderer in this case? What class should I extends to obtain all functionalities from SelectOneRadio? I would greatly appreciated if you can provided some codes.
EDIT
#BalusC: I like your idea about detecting the value of layout to delegate the correct renderer. So if I have layout="div_layout", then it works great, but if it is pageDirection or lineDirection and nothing show up. What I did is: I create a class that extends MenuRenderer and I override encodeEnd method, so in there I did this
String layout = (String) component.getAttributes().get("layout");
if(layout != null){
if(layout.equals(PAGE_DIRECTION) || layout.equals(LINE_DIRECTION)){
super.encodeEnd(context, component);
return;
} else if (!layout.equals(DIV_LAYOUT)){
//Throw error message
}
}
//Continue with my own renderer code
EDIT2
Above when I said nothing show up, I was wrong. super.encodeEnd(context, component); did render, but instead of render the radio, it render select option tag. So it seems that I delegate to the wrong renderer. I need to use RadioRenderer instead of MenuRenderer.
If it's specific to your own web application, then you could replace just alone the renderer. Easiest is to extend the implementation specific renderer and then depending on the value of one of the standard attributes (layout is the best choice) either delegate to the implementation specific renderer, or do your own custom rendering job.
I case of Mojarra, you'd like to extend com.sun.faces.renderkit.html_basic.RadioRenderer and then register it as follows
<renderkit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.example.ExtendedRadioRenderer</renderer-class>
</renderer>
</renderkit>
If you wish to be implementation independent, then you'd need to write the entire renderer implementation yourself.
If you wish to have a custom component for it, then you'd need to write it yourself as well.