How to use composite component attrs in targets - jsf

I have this piece of code:
<composite:interface componentType="nestedGridCellEditor">
<composite:attribute name="componentName" required="false" shortDescription="Name of the attribute to display in the id"/>
<composite:attribute name="action" required="false" targets="#{cc.attrs.componentName}button" shortDescription="A method expression or a string outcome to process when command is executed"/>
</composite:interface>
<composite:implementation>
<span id="#{cc.clientId}" style="display: flex;">
<p:commandButton id="#{cc.attrs.componentName}button"/>
</composite:implementation>
</span>
And I use it that way (I'm using it several of times, with a different componentName):
<sm:nestedGridCellEditor id="DateNGCE" componentName="Foo2101"
action="#{contractTermsBudgetController.updateRow('Date', budget)}"/>
What I want it to do is to use componentName inside the id of the component, and to put it inside the targets as well.
Right now it giving me an error about the targets and about the action method.
(i.e - "Unable to re-target MethodExpression as inner component referenced by target id 'button' cannot be found")
How can I do this?
EDIT
The purpose of this is because I'm using this composite inside several tables and I want to identify each button by its table's ID which should include the table name and not the clientId.
That's why I need to pass the name through the componentName.

Related

Primefaces Diagram - Generating IDs for DOM Elements

I'm trying to create a <p:diagram> with elements which contain input fields, but the problem is that for each added element, the name and ID are the same for each input field.
What I've tried to do to give each field a unique id is adding a bean-generated ID for each element, but this just gets omitted in the final code. Here's my EL/xhtml:
<p:diagram value="#{Controller.model}" style="height:600px;width:1500px" styleClass="ui-widget-content" var="lrvEl" scrollable="true">
<f:facet name="element">
<div onmouseleave="nodeOnMouseOut(this)" onclick="nodeOnClick(this)">
<h:outputText value="#{lrvEl.title}" style="display:block;margin-top:1em;width:100%;height:10px;padding-left:4px"/>
<h:outputText value="#{lrvEl.description}" style="display:block;margin-top:1em;width:100%;height:10px;padding-left:4px"/>
<h:inputText id="inputEpValue_#{lrvEl.id}" name="inputEpValue_#{lrvEl.id}" onchange="setScoreVal('#{lrvEl.id}', this)" rendered="#{lrvEl.isScore()}" style="display:block;margin-top:1em;height:10px;padding-left:4px">
</h:inputText>
</div>
</f:facet>
<p:ajax event="connect" listener="#{Controller.onConnect}" />
<p:ajax event="disconnect" listener="#{Controller.onDisconnect}" />
<p:ajax event="connectionChange" listener="#{Controller.onConnectionChange}" />
</p:diagram>
The important bit here is the <h:inputText id='inputEpValue_#{lrvEl.id}' ... > - this comes out on the page as the following:
editor:LRVContent:overlayForm_lm:inputEpValue
as if the value wasn't set, however, as you can see in the onchange field I use the same constellation. This gets rendered as onchange="setScoreVal('ep:2', this)" so that works.
Can I not dynamically set IDs for elements in this fashion? Is there another way?
Edit:
The actual Reason I want to do this is that, if I have more than one input with the same generated ID, the focus(/cursor) will automatically jump into the last generated input field when I click on any one of them, meaning I won't be able to actually change the value of any of those fields.

issue with value from bean not displaying correctly

i have a template page which has all the code i need for ten pages, where the only diff between the pages is some values
the majority of these have been working fine, however i have an issue with this section of code :
<p>
<p:spinner id="ajaxspinner0-19" value="#{tooltipBean.sectionSave}"
stepFactor = "1" min="0" max="19"
disabled = "#{formBean.number != 1}" >
<p:ajax update="ajaxspinnervalue " process="#this" />
</p:spinner>
</p>
the idea behind this code is it goes to the tooltipBean get the section save value, add this value into here and then for this to get the value from another bean
the value in the tooltipBean is
sectionSave = "#{markingBean.markToEdit.markSectionOne}";
and i use the template in the view pages by:
<f:event listener="#{tooltipBean.setupForPageA}" type="preRenderView" />
<ui:include src="/WEB-INF/templates/commonForm.xhtml"/>
i have it working normally without using the template by using :
<p>
<p:spinner id="ajaxspinner0-19" value="#{markingBean.markToEdit.markSectionOne}"
stepFactor = "1" min="0" max="19"
disabled = "#{formBean.number != 1}" >
<p:ajax update="ajaxspinnervalue " process="#this" />
</p:spinner>
</p>
that code would give me the value 71 in the spinner, however the code from the template displays in the spinner #{markingBean.markToEdit.markSectionOne} which is correct i just need this to run and return 71 rather than showing me the value of the code
does this make sense, sorry if i have written it confusingly i am unsure on how to describe the issue
i think it just not getting intalised at the right time, would it be possible to intatlise the page with the values first and then give it time for that new value to get the data in this case 71 before being viewed by the user
You cannot specify EL from a backing bean like that. The spinner control assumes that it will have both get and set access to whatever is passed in as its value attribute. It won't try to interpret the specified value as EL, which could be quite a dangerous side affect.
Here's how I would approach it. Pass in the value as a parameter to your template:
<ui:include src="templates/commonForm.xhtml">
<ui:param name="spinnerValue" value="#{markingBean.markToEdit.markSectionOne}" />
</ui:include>
Then, reference that parameter in your template:
<p>
<p:spinner id="ajaxspinner0-19" value="#{spinnerValue}"
stepFactor = "1" min="0" max="19"
disabled = "#{formBean.number != 1}" >
<p:ajax update="ajaxspinnervalue " process="#this" />
</p:spinner>
</p>

Dynamically build the column values

I am building a component that will build a data table dynamically. I need to pass in the field name from the class and concatenate it to the var attribute of the table. example : "tblVar.firstName". I have tried this using a ui:param as the below code shows, but it just prints the string expression it doesn't evaluate the firstName.
Is there a way to take a string and turn it into an EL expression.
<composite:interface>
<composite:attribute name="pageBean" type="pagecode.app.Maintenence" required="true"/>
<composite:attribute name="dataTableList"/>
<composite:attribute name="columnHeader"/>
<composite:attribute name="columnFieldName"/>
</composite:interface>
<composite:implementation>
<p:dataTable id="doc_code_table" value="#{cc.attrs.pageBean.documentCodeList}"
var="tblVar" rowIndexVar="index" paginator="false">
<ui:param value="#{tblVar}.#{cc.attrs.columnFieldName}" name="colValue"/>
<p:column headerText="#{cc.attrs.columnHeader}">
<h:outputText value="#{colValue}"/>
</p:column>
</p:dataTable>
</composite:implementation>
You're there indeed creating a string variable. The effect is exactly the same as when you do this:
<h:outputText value="#{tblVar}.#{cc.attrs.columnFieldName}" />
This is not right. You should use the brace notation #{bean[property}} to use a variable as property name. Thus, so:
<h:outputText value="#{tblVar[cc.attrs.columnFieldName]}"/>
See also:
Our EL wiki page

JSF composite:attribute with f:attribute conversion error

I'm implementing a JSF component and need to conditionally add some attributes. This question is similar to a previous JSF: p:dataTable with f:attribute results in "argument type mismatch" error, but with a completely different error message, so I raised a new question.
<composite:interface>
<composite:attribute name="filter" required="false" default="false"
type="java.lang.Boolean"/>
<composite:attribute name="rows" required="false" default="15"
type="java.lang.Integer"/>
...
</composite:interface>
<composite:implementation>
<p:dataTable ivar="p" value="#{cc.attrs.dm}">
<c:if test="#{cc.attrs.filter}">
<f:attribute name="paginator" value="#{true}"/>
<f:attribute name="rows" value="#{cc.attrs.rows}"/>
</c:if>
...
<p:dataTable>
</composite:implementation>
This results in an error java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer. Even if I manually set this, I get errors:
<f:attribute name="rows" value="15"/> ... argument type mismatch
<f:attribute name="rows" value="#{15}"/> ... java.lang.Long cannot be cast
to java.lang.Integer
If I add the attribute directly, there is no exception and the correct number of rows is diplayed:
<p:dataTable var="p" value="#{cc.attrs.dm}" rows="#{cc.attrs.rows}">
This is indeed an unfortunate corner case with numbers in EL and composite component attributes. There's no solution for this. The type information is not available in #{cc.attrs} when used in <f:attribute> and thus treated as String. The #{15} cannot be represented as an integer in EL either, all numbers are always implicitly treated as Long when the type information is absent. The ClassCastException can be prevented by using a tag file instead of a composite component.
Your best bet is doing the check in the actual rows attribute itself.
<p:dataTable ... rows="#{cc.attrs.filter ? cc.attrs.rows : null}">

JSF 2 Composite Component rendering problem

My composite component (cc) creates an inputText-Field. The important part is, that it is rendered depending on the models property "visible". The model is given to the component via parm "name".
<cc:interface>
<cc:attribute name="name" required="true"/>
</cc:interface>
<cc:implementation componentType="ch.sbi.pt.components.PMSInputText">
<h:inputText value="#{cc.attrs.name.value}" rendered="#{cc.attrs.name.visible}"/>
</cc:implementation>
In the view i have a panelGrid with 2 cells/row: the first row has a label and my cc, the second is not important. The label renders itself with the same model-property as my cc does.
<h:panelGrid columns="2">
<h:outputText value="Name" rendered="#{person.name.visible}"/>
<sbic:pmsInputText name="#{person.name}"/>
<h:outputText value="Next Label"/>
<sbic:pmsInputText name="#{something.name}"/>
</h:panelGrid>
The result (and problem) is the following, if "visible"-property returns "false":
None of the components are rendered (perfect!) BUT the cc resulting HTML leaves an empty cell (e.g. <td></td>) which results in a ugly layouted HTML-Table (offset one cell):
<table>
<tbody>
<tr>
<td></td>
<td>Next Label</td>
</tr>
....
As far as I understand this has to do with the lifecycle (jstl vs. jsf): my cc renders before the <h:outputText../> but how can i get rid of the empty cell (e.g. <td></td>)? Am i missing something here?
Thanx for your help, experts!
Marc
This is fully expected. The composite component is rendered. Only its children are not. You need to move the rendered attribute to the composite component instead.
<sbic:pmsInputText name="#{person.name}" rendered="#{person.name.visible}" />

Resources