I'm working on a JSF 2 application that is composed of a core component that can be extended by (usually client specific) code. Generally speaking: the extended parts of the application precede the core ones.
For the Java code this is done by using conventional mechanisms. For the presentation layer, we use a javax.faces.view.facelets.ResourceResolver implementation that first tries to find resources in the extension jar before the core resources are used.
We use a lot of composite components for reusable markup. Think of components for showing addresses, salaries, etc.
The Facelets are causing major headaches when it comes to the extendable nature of the application and I'm starting to wonder if there is a solution for the problems that we run into.
What we want to achieve, is having a standard interface for composite components, but override the implementation somehow by resolving multiple implementations, out of which the extended implementation should precede the core implementation.
The idea of course is, that the core defines the standard layout/templating of the application, and extensions define client-specific formatting options, or hiding/showing part of the managed model that the application does.
For example:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:pui="http://java.sun.com/jsf/composite/pui">
<cc:interface>
<cc:attribute name="saveButtonLabel" />
<cc:attribute name="saveButtonIcon" />
<cc:attribute name="saveButtonIconPosition" />
</cc:interface>
<cc:implementation>
<pui:pension_plan_custom_state pensionPlanBean="#{pensionPlanBean}" />
<pui:pension_plan_general pensionPlanBean="#{pensionPlanBean}" />
<pui:pension_plan_pension_plan pensionPlanBean="#{pensionPlanBean}" />
<pui:pension_plan_salary pensionPlanBean="#{pensionPlanBean}" />
<pui:pension_plan_investments pensionPlanBean="#{pensionPlanBean}" />
<pui:pension_plan_benefit_types benefitTypes="#{pensionPlanBean.benefitTypesViewData}" />
<div class="buttons">
<!-- Irrelevant -->
</div>
</cc:implementation>
</ui:composition>
Take the pension_plan_benefit_types composite component for example. Its interface prescribes that an attribute by the name benefitTypes is to be given by the client. If a client wants just this part of the screen to be overwritten by different content than the standard implementation would so, we need a place to overwrite this somewhere.
Also note, that the core does not know about the namespaces that the (optional) extension provides. The core doesn't really care, as long as the interface of the composite is stable and doesn't require changes.
As a last resort, the following has been tried (pseudo code):
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:pui="http://java.sun.com/jsf/composite/pui">
<cc:interface>
<cc:attribute name="benefitTypes" required="true" type="com.foo.bar.PensionPlanBenefitTypesViewData" />
</cc:interface>
<cc:implementation>
<ui:include src="/resources/pui/markup/pension_plan_benefit_types_markup.xhtml" />
</cc:implementation>
</ui:composition>
Where the idea is, that the javax.faces.view.facelets.ResourceResolver will come to our rescue.
It 'kind of works', but depending on the implementation we have to write for the composite components, we run into all kinds of issues in the infamous build,- vs rendertime department of the JSF lifecycle. It basically does not work the way we would expect it to.
The big question now is:
Is there a way in which we have both a stable contract/namespace and have multiple implementations that are dynamically resolved?
Hope someone can shed some light on this, thanks for your input.
Kind regards,
Rens
This kind of situations has been addressed in JSF 2.2 with the resource library contract feature. With that feature, it is possible to have multiple implementations of the same composite component according to its localization and active contracts. Note that the localization / contracts are active per view.
But the option that maybe suits you better is use:
<ui:include src="#{...}">
or
<ui:decorate template="#{...}">
And provide the template name from a managed bean or from the composite component class itself (cc:interface componentType=...). I would recomend use the last version of MyFaces Core in this case, because its algorithm has been optimized specifically for these cases (small view state size and fast performance). I don't think you need to deal with ResourceResolver logic here.
We have extended the ResourceHandlerWrapper in such a way that the classpath will be scanned (first) when xhtml resources from WEB-INF/resources are requested to be loaded.
By defining an explicit classpath in a MANIFEST.MF file, we can let the extension jar files take precedence over the standard implementation. By doing so, we can programmatically generate additional content too, that can be shown in the UI to the developer (when running in development mode) to let him/her know that an 'overriding' implementation could be written in the extension project.
Related
I have page where I render some h:panelGroup panels. Those panels are realized as plugins registered in a plugin registry on startup.
Part of the plugins api is a custom jsf component where I get the registered plugins for extension point and include their facelet templates by path:
<c:forEach items="#{pluginRegistry.getPlugins(point)}" var="extension">
<ui:include src="#{extension.path}" />
</c:forEach>
The page where I include the panels looks like:
<h:panelGrid id="dashboard" columns="3">
<cmf:insertPageFragments point="dashboardExtensionPoint" />
</h:panelGrid>
For every panel there are facelet templates like the one below:
<rich:panel id="caseDetailsPanel" header="panel label">
<!-- panel content -->
</rich:panel>
Now, the problem is that the very first panel in the list returned by the pluginsRegistry is rendered in the page with the provided id like formId:caseDetailsPanel for example. The rest of them have generated ids like formId:j_idt223 !!! Obviously if I want to rerender some of the panels, I can't do that.
That happens when environment is jboss AS 7.1 with JSF 2.1, richfaces 4.2.3.Final.
When deployed on jboss-eap-6.1 everything looks fine but for now I can't use this jboss version.
Any suggestions on how to workaround this issue?
There can not be multiple JSF components with the same ID. Each JSF component must have an unique ID. When dynamically creating JSF components using JSTL, you need to manually assign and ensure an unique ID, otherwise JSF will discard the provided ID and autogenerate an unique ID.
There are several ways to achieve this, depending on the concrete functional requirement and the existing code.
Use use the iteration index of <c:forEach>.
<c:forEach ... varStatus="loop">
...
<rich:panel id="caseDetailsPanel_#{loop.index}" ...>
This will generate caseDetailsPanel_0, caseDetailsPanel_1, etc depending on the current iteration index.
Use the unique identifier of the currently iterated item. It isn't clear based on the information provided so far if you have any, so here's just a fictive example assuming that the class behind #{extension} has an id property representing the technical DB identifier.
<c:forEach ... var="extension">
...
<rich:panel id="caseDetailsPanel_#{extension.id}" ...>
Wrap #1 or #2 if necessary in a <f:subview> with an unique identifier, so that you don't need to modify the includes.
<c:forEach ... varStatus="loop">
<f:subview id="panel_#{loop.index}">
<ui:include ... />
The <f:subview> creates a new NamingContainer around it, so you end up getting formId:panel_0:caseDetailsPanel, formId:panel_1:caseDetailsPanel and so on.
A completely different alternative would be to use <ui:repeat> instead of <c:forEach>. The <ui:repeat> does not run during view build time, but during view render time. This way there's physically only one <rich:panel id="caseDetailsPanel"> component in the component tree which is reused multiple times during generating HTML whereby JSF will take care of generating the right IDs with the <ui:repeat> index like so formId:repeatId:0:caseDetailsPanel. However, this in turn may cause trouble with <ui:include> as it also runs during view build time and thus can't get the #{extension} at hands.
I have composite component, in which I have toolbar and datatable. I also defined facet which contains a form for manipulating data from datatable. Users define that facet for different kinds of data. Now, I have problem because I render that facet multiple times and now I have collisions for widgetVar names for Primefaces components. It is no possible to use insertChildren multiple times so I think this is only possible solution. Also I wouldn't like to force users of component to define 10 facets and write ui:include 10 times. Is there any other way to insert some facelet code in composite component, or is there any way to pass parameter to facet, and use that parameter to dynamically create widgetVar?
OK, after some time I just didn't succeeded to do what I wanted. First I had some composite component like this:
<cc:interface>
<!-- Attributes definition -->
<cc:facet name="form"/>
</cc:interface>
<cc:implementation>
<p:dialog><f:subview id="detailSubview1"><cc:renderFacet name="form"/></f:subview></p:dialog>
<p:dialog><f:subview id="detailSubview2"><cc:renderFacet name="form"/></f:subview></p:dialog>
<!-- There is some more renderFacets but this is enough -->
</cc:implementation>
If I have for example p:selectOneMenu inside the form, without any widgetVar definitions, all will be with same name for widgetVar and this is a problem.
So, I changed this completely and I will transform this composite component to ui:composition and decorate it in my page. In that case widget vars are generated as I want, with different names, because they are in different naming containers.
A widgetVar is in fact used in JavaScript to identify the component. Therefor a widgetVar must be unique in a page. You'll have to declare it yourself.
If you want to create a custom component, as I think might suit you better than ui:define/ui:include, you might want to do something like this:
Say we want to create a component that renders a p:commandButton and a h:outputText with the same value (for whatever reason). You create a XHTML page in directory [deployed-root]/resources/example, named customComponent.xhtm:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jsf/composite">
<c:interface>
<c:attribute name="text" required="true" />
</c:interface>
<c:implementation>
<h:outputText value="#{cc.attrs.text}" />
<p:commandButton value="#{cc.attrs.text}" />
</c:implementation>
</html>
Then to use this in another page you'll have to define the namespace xmlns:e="http://java.sun.com/jsf/composite/example", and then you can refer to the custom component like this: <e:customComponent text="some text here"/>.
It should also be noted that it is bad practice to declare forms in custom components. This affects flexibility of use drastically since forms cannot be nested.
PrimeFaces can generate wigetVars so you don't have to.
From the 3.4 User's Guide:
<p:dialog id="dlg">
<!-- contents -->
</p:dialog>
<p:commandButton type="button" value="Show" onclick="#{p:widgetVar('dlg')}.show();"/>
This is designed to work in naming containers, so it should work just fine in composite components, <ui:repeat/>, <h:dataTable/>, etc.
I got the following scenario:
I got several tabs (TabView is a naming container )
and in one of the I got a p:inputText which shows a dialog(which is located in other xhtml file) , now I want the dialog to update the p:inputText , the thing is that the id of the p:inputText is unknow (JSF adds some prefix to it)
<h:form id="hoursReportFrm">
<p:inputText id="comment4Dialog" value="#{hoursReportBean.aComment}"
onfocus="dlg1.show();"/>
I can't use this update="hoursReportFrm:comment4Dialog" in the dialog
ATM i looked at the example of this site JSF: working with component IDs (id vs clientId) (its from 2009)
and added binding to to inputtext , like thisbinding="#{lookup.components.comment4Dialog}" and in the p:commandButton of the dialog I changed to update="#{lookup.clientIds.comment4Dialog}"
and It works just fine, but I'm looking for a better way , without the need to bind every component that I would like to access later...
Thanks ahead,
To be quite honest, I think the binding is probably the easiest route, however when I've been in that situation I've found composite components often offer a better solution than bindings. Depending on the situation (and again, its totally case by case) you can use a composite component to get around this problem. This allows you to reuse parts of a page creatively so that your specific updates don't take a lot of work and you can reuse the code.
An example of this might be:
//comp:myDialog
...
<composite:interface>
<composite:attribute name="update" />
<!-- Other attributes -->
</composite:interface>
<composite:implementation>
...
<!-- Implementation -->
<p:commandButton update="#{cc.attrs.update}"/>
...
</composite:implementation>
And here might be the component in use:
//for the sake of argument 'comp' as your library
<h:form id="someForm">
<p:inputText value="#{bean.changeMe}" id="changeMe"/>
</h:form>
<h:form id="dialog">
<!-- dialog here -->
<comp:myDialog update="someForm:changeMe" />
</h:form>
By separating this view into a piece of reusable code you might be able to get around the burden of specifying the full path because it is now much easier to reuse. However, I think it is a toss up of a binding or the composite component depending on the specific case. For reuse, make a new object (component) and reuse it. If you're dealing with a highly dynamic environment: bind it.
I'm not an expert by any means, but generally speaking the above has gotten me out of a lot of tough situations.
EDIT: on a re-read you should also make sure that the tab-view isn't lazily loaded and take a look at the rendering to make sure the path is correct (inspect the ID). I've had problems (in older versions of Primefaces) where sometimes the id was nested inside a p:outputPanel or in rare cases the subview id. It might be a very simple fix by specifying the full path ':subview:form:component' though that shouldn't be the case.
What I would like to achieve is to be able to address some JSF components from within other naming container.
Usecase: composite component which encapsulates some features using - for a field which is defined out of the composite component.
Some code:
<form id="foo">
...
<label for="nameTxt">Name:</label>
<component:customError forField="nameTxt" />
<h:inputText id="nameTxt" />
...
</form>
and the component:
<composite:implementation>
<h:panelGroup id="errorComponent">
...
<h:message for="#{cc.attrs.forField}" id="errorMsg" style="display:none;" />
...
</h:panelGroup>
</composite:implementation>
The problem is that on rendering the message I get:
Could not render Message. Unable to find component 'nameTxt' (calling findComponent on component 'j_id963445801_469fc056:errorMsg')
I think I understand that the problem lies in the fact the the field "nameTxt" and the message "errorMsg" lie in other naming-containers. So what I would have and like to do is to specify the path/id of "nameTxt" in relation to some common ancestor.
After studying shortly the algorithm UIComponentBase:findComponent I do not actually see any other way of adressing cross naming-containers than by specyfing whole (absolute) id-path from the root (i.e. ":foo:...:nameTxt"). And this is both clumsy and prone for errors after changing the page structure.
So - how to address properly the field "nameTxt" from within the message in the composite component?
I can reproduce your problem on MyFaces 2.1.3, but not on Mojarra 2.1.4 (and also not on older Mojarra 2.0.2). This is likely a bug in MyFaces, you'd need to report it to the MyFaces guys. In the meanwhile, I don't see any other option than (temporarily) replacing the JSF implementation by Mojarra. It has however its own share of issues as well, mainly with its broken <ui:repeat> and partial state saving implementations.
Update: I found a workaround, it's however a bit clumsy:
<component:customError forField=":#{nameTxt.clientId}" />
<h:inputText id="nameTxt" binding="#{nameTxt}" />
This will lookup using the absolute client ID instead of relative client ID. You'd only need to remove style="display:none" from your <h:message> to solve a different matter.
I have written a composite component and want to use the reserved EL #{component.clientId} to make a JQuery bind. To use this retrieved clientId in a another place in the page (outside the component), I use JSTL to store it in a view scope variable. The strange thing is that JSTL seems to prevent the natural composite component behavior of appending its id in front of its children (NamingContainer behavior). I know that JSTL is a little tricky, interfering with other components (ui:repeat for instance) because of lifecycle things, but here I don't understand this behavior.
Some concrete code is better than this long speech:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
[…]
</composite:interface>
<composite:implementation>
<rich:dataTable id="mySoLovedDataTable" […]>
#{component.clientId}
<!-- Commenting or uncommenting this line make the whole point -->
<c:set var="targetClientId" value="#{component.clientId}" scope="view" />
[…]
</rich:dataTable>
</composite:implementation>
</html>
With the line commented on, #{component.clientId} gives something like j_idt261:mySoLovedDataTable.
With the line commented out, it gives just mySoLovedDataTable.
JSTL runs during view build time. It runs at the point that JSF parses the view template into a fullworthy and renderable JSF component tree. JSF runs during view render time. It runs at the point that JSF encodes the component tree into a bunch of HTML. You can visualize it as follows: JSTL runs from top to bottom first and produces a result with JSF tags only. Then, during JSF render response phase, JSF will run from top to bottom and produce HTML result.
In other words JSTL and JSF doesn't run in sync as you'd expect from the coding. Usually you would like to use Facelets' <ui:param> instead of JSTL <c:set>.
<ui:param name="targetClientId" value="#{component.clientId}" />
Note that this does not really set anything in any scope. It merely creates kind of an "alias" for the given expression. I'm not sure if it works in your particular case the way as you intend, but as far I understand the functional requirement, you'd like to be able to obtain the client ID <rich:dataTable> further in the view, after the component. In that case, better use binding:
<rich:dataTable binding="#{table}" ...>
...
</rich:dataTable>
<script>
var $table = jQuery("[id='#{table.clientId}']");
// ...
</script>