Edited question...
Hello,
I would like to load a .xhtml file of my composite component from a backing bean, and add it to the page dynamically. The name of the .xhtml file comes form a variable.
Ex.:
public MyBean (){
String componentFile = "myCompositeComponent.xhtml"
public String addComponentToPage(){
//how do that?...
return null;
}
}
Thank you!
That's not possible. A composite component is template-based and can only be used in views. Your best bet is to repeat exactly the JSF code which you've originally written in the composite component in the model. Better would be to create a full worthy #FacesComponent class which extends UIComponent, complete with a #FacesRenderer. True, it's a tedious and opaque job, but this way you'll end up with a component which is reuseable in both the view and the model by a single code line.
An -ugly- alternative is to place all possible components in the view and use the rendered attribute.
<my:component1 rendered="#{bean.myComponentType == 'component1'}" />
<my:component2 rendered="#{bean.myComponentType == 'component2'}" />
<my:component3 rendered="#{bean.myComponentType == 'component3'}" />
...
Wrap this if necessary in a Facelets tag file so that it can be hidden away and reused in several places.
I don't understand why do you want to add a composite component from a backing bean. I guess you want to make it visible dynamically in case of an event, but for that there is AJAX reRender.
For example you can do the following:
<h:panelGroup id="composite" rendered="#{myBean.renderComponent}">
<my:compositecomponent/>
</h:panelGroup>
The renderComponent property stores a boolean value. You can switch that value and reRender composite with for e.g. Richfaces's <a4j:commandLink>.
Hope that helps, Daniel
Related
I have a need to create command links dynamically based on content coming from elsewhere.
When a user clicks on a link it should call a method in a managed bean, and the method needs to know which link was clicked.
I can create the command links using the following code:
JSF:
<h:outputText value="#{myBean.dynamicLinks}" escape="false" />
Bean:
public String getDynamicLinks(){
// Return an html string that contains a set of <a> elements, based on the dynamic content
}
This works fine, but what I can't work out is how my <a> elements can call back into the bean.
This is not the right way to "dynamically" create markup. For that you should be using XHTML, and absolutely not Java and for sure not massage some plain HTML in the model and present it with escape="false". That's plain nonsense. You're basically mingling the view into the model. You need to make the model itself dynamic, not the view. The view must be static and dumb. The view must just present the model to the world. The model itself can be dynamic. You normally achieve that by using a flexible collection, such as List<String>, List<SomeEntity>, etc which you then present using an iterator in the view such as <ui:repeat> or <h:dataTable>.
E.g.
<ui:repeat value="#{bean.links}" var="link">
<h:commandLink value="link" action="#{bean.action(link)}" />
</ui:repeat>
public void action(Link link) {
// ...
}
You see, the action method can know about the pressed link by just inspecting the method argument.
See also:
How to create dynamic JSF form fields
How can I pass selected row to commandLink inside dataTable?
I have the following scenario:
JSF composite component with complex JavaScript, which I'd like to refresh partially by JavaScript with a new values from a backing bean (backing bean of a page which uses this composite component, not backing component #FacesComponent).
I don't want to make full update because it's complex JavaScript plugin and it will unacceptably break UX.
I get values from backing component #FacesComponent by using Primefaces's <p:remoteCommand> with callback as described here Best method for passing Data from Java/JSF2 bean to Javascript/jQuery Components
I know that it's some abuse of JSF but would like to encapsulate all the functionality in a single unit and don't mess with a JAX-RS If you can advice another nice solution how to encapsulate such complex jQuery plugin (for sake of clarity we are talking about FullCalendar, I know that Primefaces has its own implementation of this component but its functionality insufficient for my requirement so, I was need to make my own implementation) which highly related on ajax callbacks with parameters you're highly welcome to share it here.
My question is how to update values in a backing component #FacesComponent from backing bean by using JavaScript? Currently I involved in the following chain of events:
calling from Javascript <p:remoteCommand> with parameters which passed to backing component #FacesComponent to be dispatched later in AjaxBehaviorEvent
JavaScript:
refreshEvents([
{name:'start', value:start.format()},
{name:'end', value:end.format()}
]);
JSF code:
<p:remoteCommand name="refreshValues" oncomplete="loadValues()" action="#{cc.refreshLocal()}" process="#this"/>
Parameters which I passed stored in a backing component by using
getStateHelper().put(...);
jQuery event dispatched from composite component by following JavaScript code:
var hiddenField = $(document.getElementById(variables.hiddenId));
hiddenField.trigger("keypress");
In composite component's overridden method public void queueEvent(FacesEvent event) I add to this AjaxBehaviorEvent property which I stored before, in a 1st step and dispatch it forward.
Dispatched event from composite component "captured" in a page where composite component nested and performed process on this component:
<p:ajax event="refreshEvent" process="#this" listener="#{bean.refreshEvents}"/>
in #{bean.refreshEvent} method I perform request to #EJB bean and load data.
On callback from step 1 called again by loadValues()
<p:remoteCommand name="loadValues" action="#{cc.getLocalData()}" oncomplete="updateValues(xhr, status, args);"/>
In a backing component's method #{cc.getLocalData()} I add a callback parameter by using:
RequestContext.getCurrentInstance().addCallbackParam("param", ...);
function updateValues(xhr, status, args) from step 5 get in args this param's values and performs actual update.
So, my general question is it possible to simplify this process and how?
Thank you.
This is indeed a little overcomplicated. In total 3 ajax requests to just perform an action and a backing component passing data forth and back via the view state.
Your primary goal appears to be able to declare a bean action method as composite component attribute which should then be invoked by a <p:remoteCommand> inside the composite and return the desired model entity based on the passed-in parameters, preferably with some pre- and post-processing.
You can use <cc:attribute method-signature> in the composite interface to declare a method expression attribute:
<cc:interface componentType="yourComposite">
<cc:attribute name="eventListener" method-signature="com.example.Entity method(java.lang.String, java.lang.String)" required="true" />
</cc:interface>
Which can be used in the template client as below:
<my:composite ... eventListener="#{bean.eventListener}" />
public Entity eventListener(String start, String end) {
// ...
return entity;
}
The composite implementation can look like this:
<cc:implementation>
...
<p:remoteCommand name="refreshEvents"
process="#this" action="#{cc.processEventListener}"
oncomplete="updateValues(xhr, status, args)" />
...
</cc:implementation>
The #{cc.processEventListener} method can obtain the eventListener attribute as a MethodExpression and invoke it as below, along with some pre- and post-processing:
public void processEventListener() {
String start = getRequestParameter("start");
String end = getRequestParameter("end");
// ...
MethodExpression method = (MethodExpression) getAttributes().get("eventListener");
Entity entity = (Entity) eventListener.invoke(getFacesContext().getELContext(), new Object[] { start, end });
// ...
addCallbackParam("param", entityAsJSON);
}
Now it's only 1 ajax request "as usual".
Project uses Spring Webflow and JSF (PrimeFaces). I have a p:commandButton with f:attribute
<p:commandButton disabled="#{editGroupMode=='edit'}" action="edit_article_group" actionListener="#{articleGroupManager.setSelectedRow}" ajax="false" value="Edit">
<f:attribute name="selectedIndex" value="${rowIndex}" />
</p:commandButton>
Backend code (Spring injected bean):
#Service("articleGroupManager")
public class ArticleGroupManagerImpl implements ArticleGroupManager{
public void setSelectedRow(ActionEvent event) {
String selectedIndex = (String)event.getComponent().getAttributes().get("selectedIndex");
if (selectedIndex == null) {
return;
}
}
}
The attribute "selectedIndex" is always null. Anybody knows what happened here? Thank you.
The variable name "rowIndex" suggests that you've declared this inside an iterating component, such as <p:dataTable>.
This is then indeed not going to work. There's physically only one JSF component in the component tree which is reused multiple times during generating HTML output. The <f:attribute> is evaluated at the moment when the component is created (which happens only once, long before iteration!), not when the component generates HTML based on the currently iterated row. It would indeed always be null.
There are several ways to achieve your concrete functional requirement anyway. The most sane approach would be to just pass it as method argument:
<p:commandButton value="Edit"
action="edit_article_group"
actionListener="#{articleGroupManager.setSelectedRow(rowIndex)}"
ajax="false" disabled="#{editGroupMode=='edit'}" />
with
public void setSelectedRow(Integer rowIndex) {
// ...
}
See also:
JSTL in JSF2 Facelets... makes sense?
How can I pass selected row to commandLink inside dataTable?
Unrelated to the concrete problem, I'd in this particular case have used just a GET link with a request parameter to make the request idempotent (bookmarkable, re-executable without impact in server side, searchbot-crawlable, etc). See also Communication in JSF 2.0 - Processing GET request parameters.
I searched everywhere but could not find a solution to this. I am trying to used
required=yes to validate whether a value is present or not. I am using it inside inputtext.
The problem is it does not work inside a datatable. If I put the text box outside the datatable it works. I am using JSF 1.7 so I don't have the validateRequired tag from JSF 2.0.
I even used a validator class but it is still not working. Does anyone know why does required=yes or validator='validationClass' inside a inputtext inside a datatable is not working.
I appreciate the help.
Thanks.
First of all, the proper attribute values of the required attribute are the boolean values true or false, not a string value of Yes. It's an attribute which accepts a boolean expression.
The following are proper usage examples:
<h:inputText required="true" />
<h:inputText required="#{bean.booleanValue}" />
<h:inputText required="#{bean.stringValue == 'Yes'}" />
As to the problem that it doesn't work inside a <h:dataTable>, that can happen when the datamodel is not been preserved properly (the datamodel is whatever the table retrieves in its value attribute). That can in turn happen when the managed bean is request scoped and doesn't prepare the datamodel during its (post)construction which causes that the datamodel is null or empty while JSF is about to gather, convert and validate the submitted values.
You need to ensure that the datamodel is exactly the same during the apply request values phase of the form submit request as it was during the render response phase of the initial request to display the form with the table. An easy quick test is to put the bean in the session scope. If that fixes the problem, then you definitely need to rewrite the datamodel preserving logic. You could also use Tomahawk's <t:saveState> or <t:dataTable preserveDataModel="true"> to store the datamodel in the view scope (like as JSF2's new view scope is doing).
Finally, JSF 1.7 doesn't exist. Perhaps you mean JSF 1.2?
I declare my repeater in a form (lets say in main.xhtml):
<!--<itemRenderer:MetaFieldRenderer item="{kueditmeta.metaFieldFirst}" >
</itemRenderer:MetaFieldRenderer>-->
<ui:repeat var="foo" value="#{kueditmeta.metaFields}" >
<itemRenderer:MetaFieldRenderer item="#{foo}" >
</itemRenderer:MetaFieldRenderer>
</ui:repeat>
My form on submit inspect my MetaFieldRenderer.xhtml composite component in the repeater, I look at an attribute: <cc:attribute name="item" /> by calling MetaFieldRenderer().getModel();
In my FacesComponent back bean I expose getModel() method:
public MetaFieldModel getModel() {
ELContext el = this.getFacesContext().getELContext();
ValueExpression valExp = this.getValueExpression("item");
Object itemValue = valExp.getValue(el);
return (MetaFieldModel) itemValue;
}
itemValue is always null when I nest MetaFieldRenderer in a UIRepeat. But if I uncomment the one outside of the repeater itemValue is not null.
Anyone knows what's going on? Is there a better way to bind from and attribute to my FacesComponent? Am I missing a necessary implementation in my component so the UIRepeat do update its children?
(I use JSF 2.0.3)
I expected the components to populate themselves at pretty much the same phase (or at least before Invoke application (aka: action, navigateTo...) phase). But UIRepeat at Invoke Application phase always contained only one child, the itemRenderer it would use for repeating, the data was not repeated yet (Ho it happen at render phase I guess?).
I came to understand something through those steps:
Restore View phase bring back a plain UIRepeat and its children declare in the xhtml. No UIRepeat doesn't populate anything yet, there is only the itemRenderer without any value in it at that point.
To see how its children (if composite component) are not bound to anything at that moment use the required attribute <cc:attribute required="true"... its gonna throw an error even if you gave the attribute because uirepeat doesn't pass them.
Lesson learned, don't do logic/validation on the composite component backbean, just reflect the model. Now I drive my repeater with a list of model that can validate itself and if the model contains a warn message or whatever fancy stuff I just display it in the composite component.